ฉันจะจับคู่สตริงกับ regex ใน Bash ได้อย่างไร


166

ฉันพยายามที่จะเขียนสคริปต์ทุบตีที่มีฟังก์ชั่นดังนั้นเมื่อได้รับ.tar, .tar.bz2, .tar.gzไฟล์อื่น ๆ จะใช้ tar กับสวิทช์ที่เกี่ยวข้องเพื่อขยายไฟล์

ฉันใช้ถ้า elif แล้วข้อความที่ทดสอบชื่อไฟล์เพื่อดูว่ามันลงท้ายด้วยและฉันไม่สามารถให้ตรงกับ metacharacters regex

หากต้องการบันทึกการเขียนสคริปต์ใหม่อย่างต่อเนื่องที่ฉันใช้ 'ทดสอบ' ที่บรรทัดคำสั่งฉันคิดว่าคำสั่งด้านล่างควรใช้งานได้ฉันได้ลองใช้ชุดวงเล็บเครื่องหมายคำพูดและ metacharaters ทุกอย่างที่เป็นไปได้ แต่ก็ยังล้มเหลว

test sed-4.2.2.tar.bz2 = tar\.bz2$; echo $?
(this returns 1, false)

ฉันแน่ใจว่าปัญหาเป็นเรื่องง่ายและฉันได้ดูทุกที่ แต่ฉันไม่สามารถเข้าใจได้ว่าจะทำอย่างไร มีใครรู้บ้างว่าฉันจะทำสิ่งนี้ได้อย่างไร

คำตอบ:


268

เพื่อให้ตรงกับ regexes คุณต้องใช้=~ผู้ประกอบการ

ลองสิ่งนี้:

[[ sed-4.2.2.tar.bz2 =~ tar.bz2$ ]] && echo matched

หรือคุณสามารถใช้อักขระตัวแทน (แทนที่จะเป็น regexes) กับ==โอเปอเรเตอร์:

[[ sed-4.2.2.tar.bz2 == *tar.bz2 ]] && echo matched

หากการพกพาไม่ใช่เรื่องที่น่ากังวลฉันแนะนำให้ใช้[[แทน[หรือtestเพราะปลอดภัยและมีประสิทธิภาพมากกว่า ดูความแตกต่างระหว่างการทดสอบคืออะไร [และ [[? เพื่อดูรายละเอียด


7
ระวังการจับคู่สัญลักษณ์ตัวแทนในตัวอย่างที่สอง ภายใน [[]], * จะไม่ขยายตามปกติเพื่อจับคู่ชื่อไฟล์ในไดเรกทอรีปัจจุบันที่ตรงกับรูปแบบตัวอย่างงานของคุณ แต่จริง ๆ แล้วมันง่ายเกินไปที่จะพูดเกินจริงและเชื่อผิด ๆ ว่า * หมายถึงการจับคู่สิ่งใด ๆ ใน บริบทใด ๆ มันใช้งานได้เช่นนั้นภายใน [[]] มิฉะนั้นจะขยายไปยังชื่อไฟล์ที่มีอยู่
Alan Porter

7
ฉันพยายามใช้อัญประกาศใน regex และล้มเหลว; คำตอบนี้ช่วยในการทำงานนี้check="^a.*c$";if [[ "abc" =~ $check ]];then echo match;fiเราต้องเก็บ regex ใน var
Aquarius Power

นอกจากนี้โปรดทราบว่า regexp (เหมือนใน perl) จะต้องไม่อยู่ในวงเล็บ: [[ sed-4.2.2.tar.bz2 == "*tar.bz2" ]]จะไม่ทำงาน
pevik

18
FWIW ไวยากรณ์สำหรับการปฏิเสธ (คือไม่ตรง ) [[ ! foo =~ bar ]]คือ
Skippy le Grand Gourou

1
dash ไม่สนับสนุน-n 1พารามิเตอร์และไม่ได้ใส่พารามิเตอร์ลงใน$REPLYตัวแปรโดยอัตโนมัติ ระวัง!

54

ฟังก์ชั่นในการทำเช่นนี้

extract () {
  if [ -f $1 ] ; then
      case $1 in
          *.tar.bz2)   tar xvjf $1    ;;
          *.tar.gz)    tar xvzf $1    ;;
          *.bz2)       bunzip2 $1     ;;
          *.rar)       rar x $1       ;;
          *.gz)        gunzip $1      ;;
          *.tar)       tar xvf $1     ;;
          *.tbz2)      tar xvjf $1    ;;
          *.tgz)       tar xvzf $1    ;;
          *.zip)       unzip $1       ;;
          *.Z)         uncompress $1  ;;
          *.7z)        7z x $1        ;;
          *)           echo "don't know '$1'..." ;;
      esac
  else
      echo "'$1' is not a valid file!"
  fi
}

หมายเหตุอื่น ๆ

ในการตอบสนองต่ออำนาจกุมภ์ในความคิดเห็นข้างต้น We need to store the regex on a var

ตัวแปร BASH_REMATCH ถูกตั้งค่าหลังจากที่คุณจับคู่นิพจน์แล้วและ $ {BASH_REMATCH [n]} จะตรงกับกลุ่มที่ n ที่อยู่ในวงเล็บเช่นในต่อไปนี้${BASH_REMATCH[1]} = "compressed"และ${BASH_REMATCH[2]} = ".gz"

if [[ "compressed.gz" =~ ^(.*)(\.[a-z]{1,5})$ ]]; 
then 
  echo ${BASH_REMATCH[2]} ; 
else 
  echo "Not proper format"; 
fi

(regex ด้านบนไม่ได้หมายถึงการใช้งานที่ถูกต้องสำหรับการตั้งชื่อไฟล์และนามสกุล แต่มันใช้งานได้สำหรับตัวอย่าง)


โปรดทราบว่าด้วย BSD tar คุณสามารถใช้ "tar xf" สำหรับทุกรูปแบบและไม่ต้องการคำสั่งแยกต่างหากหรือฟังก์ชั่นนี้ใด ๆ
คนดี

aบน GNU tar หรือpบน BSD tar เพื่อบอกให้ชัดแจ้งโดยอัตโนมัติประเภทการบีบอัดจากส่วนขยาย tar GNU จะไม่ทำมันโดยอัตโนมัติและฉันคาดเดาจากความเห็นของ @GoodPerson ที่ BSD tar ทำตามค่าเริ่มต้น
Mark K Cowan

7z สามารถแกะออกได้ .. AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, SquashFS , UDF, UEFI, VDI, VHD, VMDK, WIM, XAR และ Z ดู7-zip.org
mosh

14

ฉันไม่มีตัวแทนมากพอที่จะแสดงความคิดเห็นที่นี่ดังนั้นฉันจึงส่งคำตอบใหม่เพื่อปรับปรุงคำตอบของ dogbane จุด ใน regexp

[[ sed-4.2.2.tar.bz2 =~ tar.bz2$ ]] && echo matched

จะจับคู่กับอักขระใด ๆ ตัวอย่างเช่นไม่เพียง แต่ตัวอักษรจุดระหว่าง 'tar.bz2' เท่านั้น

[[ sed-4.2.2.tar4bz2 =~ tar.bz2$ ]] && echo matched
[[ sed-4.2.2.tar§bz2 =~ tar.bz2$ ]] && echo matched

หรือสิ่งใดก็ตามที่ไม่ต้องการการหลบหนีด้วย '\' ไวยากรณ์ที่เข้มงวดควรเป็น

[[ sed-4.2.2.tar.bz2 =~ tar\.bz2$ ]] && echo matched

หรือคุณสามารถเข้มงวดยิ่งขึ้นและรวมถึงจุดก่อนหน้าใน regex:

[[ sed-4.2.2.tar.bz2 =~ \.tar\.bz2$ ]] && echo matched

9

เนื่องจากคุณกำลังใช้ bash คุณไม่จำเป็นต้องสร้างกระบวนการลูกสำหรับการทำเช่นนี้ นี่คือทางออกหนึ่งที่ทำงานได้อย่างสมบูรณ์ภายในทุบตี:

[[ $TEST =~ ^(.*):\ +(.*)$ ]] && TEST=${BASH_REMATCH[1]}:${BASH_REMATCH[2]}

คำอธิบาย: กลุ่มก่อนและหลังลำดับ "โคลอนและหนึ่งช่องว่างหรือมากกว่า" ถูกจัดเก็บโดยโอเปอเรเตอร์การจับคู่รูปแบบในอาร์เรย์ BASH_REMATCH


1
โปรดทราบว่าดัชนี 0 มีการจับคู่แบบเต็มและดัชนี 1 และ 2 มีการจับคู่กลุ่ม
Rainer Schwarze

3
if [[ $STR == *pattern* ]]
then
    echo "It is the string!"
else
    echo "It's not him!"
fi

ใช้งานได้สำหรับฉัน! GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)


1
สิ่งนี้เป็นอันตรายอย่างยิ่ง มันจะทำงานโดยไม่มีพฤติกรรมที่ไม่ได้กำหนดสำหรับคุณเนื่องจากคุณไม่มีไฟล์ในไดเรกทอรีปัจจุบันชื่อ "รูปแบบ" ตัวอักษรย่อย ไปข้างหน้าสร้างไฟล์บางชื่อเช่นนั้นและการขยายซับสตริงจะตรงกับไฟล์และทำลายทุกอย่างอย่างน่ากลัวด้วย heisenbugs หลากสี
i336_

แต่ฉันได้ทำการทดสอบแล้ว: ด้วยไฟล์ `1 รูปแบบ , pattern pattern2 และ pattern ในไดเรกทอรีปัจจุบัน สคริปต์นี้ทำงานตามที่คาดไว้ คุณช่วยบอกผลการทดสอบของคุณให้ฉันได้ไหม @ i336_
juan cortez

2
@ i336: ฉันไม่คิดอย่างนั้น ภายใน[[ ... ]]รูปแบบ rhs glob ไม่ได้ขยายไปตามไดเรกทอรีปัจจุบันเหมือนที่เคยทำ
user1934428

@ i336_ ไม่ภายใน[[...]]Bash ไม่ทำการขยายชื่อไฟล์ ในคู่มือทุบตีWord splitting and filename expansion are not performed on the words between the [[ and ]];
jinbeom hong

@jinbeomhong: TIL ขอบคุณมากที่รู้!
i336_

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.