การตั้งค่าใน bash สำหรับ globbing คืออะไรเพื่อควบคุมว่า * ตรงกับไฟล์ dot หรือไม่


12

ฉันประหลาดใจเมื่อเร็ว ๆ นี้เมื่อฉันทำสิ่งที่ชอบ mv ./* ../somedirectory และพบว่าไฟล์เช่น.gitignoreนั้นไม่ได้ถูกย้าย

ฉันทำงานส่วนใหญ่ของฉันใน zsh บน OS X และสิ่งนี้ทำให้ฉันประหลาดใจในการทุบตี CentOS ฉันพยายามทุบตีใน OS X และพบพฤติกรรมเดียวกัน: *ไม่ตรงกับไฟล์ dot ดูเหมือนว่าฉันจะไม่พึงประสงค์มาก แต่ดูเหมือนว่ามันเป็นค่าเริ่มต้นของ bash (อาจเป็นค่าเริ่มต้น zsh ด้วยสำหรับทุกสิ่งที่ฉันจำได้ แต่ฉันอาจเปลี่ยนไปเมื่อหลายปีก่อนใน. zshrc ของฉันและลืมว่ามันใช้งานได้แตกต่างกันไป)

ฉันจะกำหนดค่า bash ให้ทำงานตามที่คาดไว้ได้อย่างไร: สำหรับ * เพื่อให้ตรงกับไฟล์ทั้งหมดและไม่ละเว้นไฟล์ dot

ในกรณีที่ไม่ชัดเจนนี่คือวิธีการทำซ้ำ

cd /tmp
mkdir {t,d}est
touch test/{.,}{1,2,3,4,5,6,7}
ls -hal test
mv test/* dest
ls -hal test     # notice dot files are still there
ls -hal dest     # notice only some files were mv'ed


ใช่พวกเขาเกี่ยวข้องกัน ที่ไม่ปรากฏขึ้นเมื่อฉันค้นหา (อาจเป็นเพราะผู้ถามถึงที่ไม่ได้พูดถึงไฟล์ globbing หรือจุด) แต่มันเป็นคำถามที่แตกต่างกันจริงๆ เขาถามว่าจะย้ายไฟล์อย่างไรและฉันถามวิธีเปลี่ยนพฤติกรรมของเชลล์และเฉพาะสำหรับการทุบตี ฉันอาจไม่ได้ถามว่าคำถามนั้นปรากฏขึ้นมาเพื่อฉัน (เนื่องจากคำตอบที่สมบูรณ์และครบถ้วนของคุณมีการตั้งค่า bash) แต่ก็ยังเป็นคำถามที่แตกต่างกันและคนที่กำลังมองหาคำตอบสำหรับคำถามของฉันไม่จำเป็นต้องไปหา คำถามของเขา
iconoclast

ทั้งหมด“ ใครบางคนกำลังมองหาคำตอบสำหรับคำถามของฉันไม่จำเป็นต้องไปหาคำถามของเขา” นั่นคือเหตุผลที่ว่าทำไมเรามีวิธีปิดคำถามแบบนี้ซ้ำซ้อน
Gilles 'หยุดความชั่วร้าย'

ใช่ แต่ดูเหมือนว่าคุณจะพลาดประเด็นหลักซึ่งก็คือคำถามอื่น
iconoclast

คำตอบ:


14

ทุบตี

ในขณะที่คุณสังเกตเห็นทุบตีจะไม่ตรงกับ.ที่จุดเริ่มต้นของชื่อหรือเครื่องหมายทับ ในการเปลี่ยนการจับคู่เกี่ยวกับจุดคุณต้องตั้งค่าdotglobตัวเลือก - man bash :

dotglob ถ้าตั้งค่า bash จะมีชื่อไฟล์ที่ขึ้นต้นด้วย `. ' ใน
    ผลลัพธ์ของการขยายชื่อพา ธ

เพื่อเปิดใช้งาน / ตั้งค่าด้วยการใช้ bash shoptเช่น:

shopt -s dotglob


สำหรับ zsh คุณยังสามารถใช้dotglobตัวเลือกได้ แต่คุณจะต้องใช้setoptเพื่อเปิดใช้งานเช่น:

setopt dotglob

2
ด้วยzshตัวเลือกที่ดีกว่าคือการเปิดใช้งานdotglobบนพื้นฐานแบบต่อเนื่อง:mv test/*(D) /dest
Stéphane Chazelas

7

ฉันทดสอบสิ่งนี้และมันแก้ปัญหาได้:

shopt -s dotglob

เอาท์พุท:

~/stackexchangeanswers/40662$ ls -hal dest
total 8.0K
drwxr-xr-x 2 jodiec jodiec 4.0K 2012-06-12 22:15 .
drwxr-xr-x 4 jodiec jodiec 4.0K 2012-06-12 22:15 ..
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 1
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .1
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 2
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .2
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 3
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .3
...snipped....

ขออภัยที่ Ulrich ตอบก่อน
iconoclast

3
พวกเราทุกคนอยู่ในทีมเดียวกัน ไม่เป็นไร.
Jodie C

6

*เป็น glob ที่ถูกขยายโดยเชลล์ โดยค่าเริ่มต้นหอยไม่รวมไฟล์ที่มีชื่อขึ้นต้นด้วย.(เรียกว่าไฟล์ที่ซ่อนอยู่หรือ dotfiles) เว้นแต่ว่าชั้นนำ.จะถูกป้อนอย่างแท้จริง

*หรือ[.]*หรือ?*หรือ*.*หรือdir/*จะไม่รวม dotfiles

.*หรือdir/.*จะ

ดังนั้นคุณสามารถทำได้:

mv -- * .* /dest/

อย่างไรก็ตามเชลล์บางตัวรวมถึงbash(แต่ไม่ใช่zsh, mkshและfish) มีความผิดพลาดที่การขยายของ.*รวม.และ..รายการไดเร็กทอรีพิเศษ, ซึ่งคุณไม่ต้องการที่นี่ (และโดยทั่วไปไม่ต้องการให้ glob รวมซึ่งเป็นเหตุผลที่ฉันเรียกมันว่า

ด้วยเหตุผลดังกล่าวคุณจะพบว่าบางครั้งผู้คนใช้ (ในเชลล์คล้ายบอร์น):

mv -- * .[!.]* ..?* /dest/

นั่นคือสาม globs หนึ่งตรงกับไฟล์ที่ไม่ซ่อนแรกคนที่สองชื่อไฟล์ที่เริ่มต้นด้วย.ที่ตามตัวอักษรอื่นที่ไม่ใช่.และ 3 หนึ่งชื่อไฟล์ที่เริ่มต้นด้วย..ที่ตามตัวอักษรอย่างน้อยหนึ่ง

อย่างไรก็ตามกระสุนสมัยใหม่บางตัวมีวิธีที่ดีกว่า

zsh

ด้วยzshคุณสามารถใช้ตัว(D)ระบุแบบกลมเพื่อระบุว่ารูปแบบวงกลมควรมีดอทไฟล์

mv -- *(D) /dest/

zshยังแก้ไขว่าการบอร์เชลล์เชลล์ผิดพลาดอื่น ๆ ในกรณีที่รูปแบบไม่ตรงกันmvคำสั่งจะไม่ทำงาน

ดังที่ได้กล่าวไว้ข้างต้นมันจะไม่รวม.หรือ..อยู่ในความโกลาหลดังนั้น

mv -- * .* /dest/

จะปลอดภัย อย่างไรก็ตามหากไม่มีไฟล์ที่ตรงกัน*หรือไม่มีไฟล์ที่ตรง.*กับคำสั่งจะถูกยกเลิกดังนั้นควรใช้:

mv -- (*|.*) /dest/

เช่นเดียวกับในเปลือกหอยอื่น ๆ คุณสามารถบังคับ globs ทั้งหมดให้รวม dotfiles (ตัวอย่างเช่นถ้าคุณพบว่าตัวเองต้องการ dotfiles รวมบ่อยกว่า) ด้วย:

setopt dotglob

หรือ:

set -o dotglob

หลังจากนั้นหากคุณต้องการให้ glob เฉพาะไม่รวม dotfiles คุณสามารถเขียนมันได้:

echo *(^D)

หรือ:

echo [^.]*

ทุบตี

น่าเสียดายที่bashไม่มีตัวระบุ glob ดังนั้นคุณเหลือที่เปิดใช้งานการรวม dotfile ทั่วโลก ในbashไวยากรณ์คือ:

shopt -s dotglob

(และใช้[^.]*สำหรับ globs โดยไม่มีไฟล์ที่ซ่อนอยู่)

ด้วยdotglob, bashไม่รวม.มิได้..ใน globs เหมือน*แต่ยังคงไม่สำหรับ globs .*เช่น

หากคุณตั้งค่าGLOBIGNOREตัวแปรเป็นสิ่งที่ไม่ว่างเปล่ามันจะเปิดใช้งานdotglobตัวเลือกโดยอัตโนมัติและแยก.และ..ออกจาก.*globs แต่ไม่ใช่จากdir/.*หรือ.*/fileวัตถุ (!) เพื่อให้การป้องกันไม่มีประโยชน์ คุณสามารถทำGLOBIGNORE='*/.:*/..:./*:../*:*/./*:*/../*'แต่แล้วมันจะทำลาย globs เหมือน*/.หรือหรือ./*../*

ทำงานได้ดีขึ้นรอบ ๆ คือการใช้[.]*หรือdir/[.]*หรือ[.]*/file(ที่มีdotglobการเปิดใช้งาน) เพื่อขยาย dotfiles ยกเว้นและ...

ปลา

fishglobs ไม่รวมมิได้. ..เมื่อไม่มีการแข่งขันขึ้นอยู่กับรุ่นก็จะทำงานเหมือนอย่างใดอย่างหนึ่งzsh(หรือbash -o failglob) bash -o nullglobหรือ

mv -- * .* /dest/

จะทำงานหากมีทั้งไฟล์ที่ซ่อนและไม่ซ่อน มิฉะนั้น YMMV และในบางเวอร์ชันอาจเรียกได้mv -- /destว่าไม่มีไฟล์เลย

ksh93

ไม่มีตัวระบุแบบกลมในตัวksh93ใดตัวหนึ่ง คุณสามารถรวม dotfiles ใน globs ด้วย:

FIGNORE='@(.|..)'

ขัดกับbash's GLOBIGNORE, ที่ทำอย่างถูกต้องและยังแก้ไขปัญหาของการ.*รวมและ...

Yash

yashมีdot-globตัวเลือก ( set -o dot-glob) แต่ตรงกันข้ามกับbashการขยาย glob (แม้กระทั่ง*) รวมถึง.และ..ดังนั้นจึงไม่มีประโยชน์สวย

tcsh

set globdot

ธิเช่นเดียวกับในbashที่มี*รวมถึงไฟล์ dot ยกเว้น.และ..แต่.*ยังรวมถึง.และ..(และคุณสามารถใช้[.]*เพื่อขยายไฟล์ที่ซ่อนยกเว้น.และ..)


4

วิธีที่ง่ายที่สุดในการทุบตีเพื่อพิมพ์ไฟล์ dot ที่ตรงกันโดยไม่สนใจ.และ..คือ:

$ GLOBIGNORE=/+/
$ printf '%s\n' *


ทดสอบ:

$ cd tmp; mkdir empty; cd empty; touch {,.}{a..h}
$ GLOBIGNORE=/+/
$  ls *
a  .a  b  .b  c  .c  d  .d  e  .e  f  .f  g  .g  h  .h

มันพิมพ์ไฟล์ทั้งหมดที่มีจุดหรือไม่มีข้อยกเว้นเด่นและ...

ตัวอย่างของคุณจะทำงานอย่างถูกต้อง:

$ cd /tmp; mkdir {t,d}est; touch test/{,.}{a..h}
$ mv test/* dest/
$ ls -a test
.  ..
$ ls -a test/*
ls: cannot access test/*: No such file or directory

ไม่มีไฟล์ที่มีความสำคัญ
ไฟล์ระบบ. ..ยังคงมีอยู่ แต่การขยายเชลล์ (การใช้ *) จะไม่รวมไฟล์เหล่านั้น

และไดเร็กทอรีdest ination มีไฟล์ทั้งหมด:

$ cd dest
$ ls -a *
a  .a  b  .b  c  .c  d  .d  e  .e  f  .f  g  .g  h  .h

ทำไม?

ใช้งานได้เนื่องจากการตั้งค่า GLOBIGNORE มีผลข้างเคียงนี้:

  • ตั้งค่า dotglob ( shopt -s dotglob) เป็นไฟล์คณิตศาสตร์ dot เป็นส่วนขยาย
  • ชื่อไฟล์. และ..จะถูกเพิกเฉยเมื่อตั้งค่า GLOBIGNORE ไม่ใช่ null

LESS=+'/The GLOBIGNORE' man bashรายละเอียดในคู่มือ:

นอกจากนี้เราต้องตั้งค่าตัวแปร GLOBIGNORE เพื่อให้มีชื่อที่ไม่มีชื่อไฟล์ที่ตรงกัน:
เครื่องหมายทับ (และอย่างอื่นที่เป็นข้อควรระวังสองครั้ง)

$ GLOBIGNORE=/+/

มันอาจจะเป็นประโยชน์ที่จะยังชุด nullglob ถ้ามันเป็นสิ่งจำเป็นเพื่อหลีกเลี่ยงการได้รับเมื่อมีไฟล์เพื่อให้ตรงกับไม่มี**


0

สนใจการตั้งค่านี้เป็นอย่างมากของ GLOBIGNORE = / + / @ user79743

จากประสบการณ์ของผมunsetting dotglob เป็นธุรกิจที่ไม่ดีไปเกือบทุกคำสั่งของคุณ (CP, mv ฯลฯ ) แต่เดียวกันเป็นจริงการตั้งค่า nullglob (โดยเฉพาะอย่างยิ่งมีการแสดงออกปกติแล้วต้องหลบหนี!)

อย่างไรก็ตามหากคุณต้องการตั้งค่าเหล่านี้ในตอนเริ่มต้นโปรแกรมของคุณ แต่รบกวนส่วนเฉพาะที่คุณเรียกใช้คำสั่งเช่น cp, mv เป็นต้นหรือใช้นิพจน์ทั่วไปเพียงแค่ทำสิ่งนี้:

  # set dotglob, unset nullglob AFTER this
  is_nullglob=$( shopt -s | egrep -i '.*nullglob' )
  is_dotglob=$( shopt -s | egrep -i '.*dotglob' )
  [[ $is_nullglob ]] && shopt -u nullglob
  [[ ! $is_dotglob ]] && shopt -s dotglob
  # ... call commands ...
  # .....................
  # reset dotglob, nullglob to their previous values
  [[ $is_nullglob ]] && shopt -s nullglob
  [[ ! $is_dotglob ]] && shopt -u dotglob
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.