จะส่งอักขระตัวแทน '*' ไปยังพารามิเตอร์ path ของคำสั่ง find ผ่านตัวแปรในสคริปต์ได้อย่างไร


9

ฉันต้องการใช้findเพื่อค้นหาไฟล์ในชุดของโฟลเดอร์ที่ จำกัด ด้วยอักขระตัวแทน แต่ในกรณีที่มีช่องว่างในชื่อพา ธ

จากบรรทัดคำสั่งนี่เป็นเรื่องง่าย ตัวอย่างต่อไปนี้ใช้ได้ทั้งหมด

find  te*/my\ files/more   -print
find  te*/'my files'/more  -print
find  te*/my' 'files/more  -print

เหล่านี้จะหาไฟล์ในตัวอย่างเช่น, และterminal/my files/moretepid/my files/more

อย่างไรก็ตามฉันต้องการสิ่งนี้เพื่อเป็นส่วนหนึ่งของสคริปต์ สิ่งที่ฉันต้องการคืออะไรเช่นนี้:

SEARCH='te*/my\ files/more'
find ${SEARCH} -print

น่าเสียดายที่สิ่งที่ฉันทำฉันไม่สามารถผสม wildcard และช่องว่างในfindคำสั่งภายในสคริปต์ได้ ตัวอย่างข้างต้นจะส่งกลับข้อผิดพลาดต่อไปนี้ (ให้สังเกตเครื่องหมายแบ็กสแลชสองเท่าที่ไม่คาดคิด):

find: te*/my\\’: No such file or directory
find: files/more’: No such file or directory

การพยายามใช้อัญประกาศก็ล้มเหลวเช่นกัน

SEARCH="te*/'my files'/more"
find ${SEARCH} -print

สิ่งนี้ส่งคืนข้อผิดพลาดต่อไปนี้โดยไม่สนใจความหมายของเครื่องหมายคำพูด:

find: te*/'my’: No such file or directory
find: ‘files'/more’: No such file or directory

นี่คืออีกตัวอย่างหนึ่ง

SEARCH='te*/my files/more'
find ${SEARCH} -print

อย่างที่คาดไว้:

find: te*/my’: No such file or directory
find: files/more’: No such file or directory

ทุกรูปแบบที่ฉันได้ลองส่งคืนข้อผิดพลาด

ฉันมีวิธีแก้ปัญหาซึ่งอาจเป็นอันตรายได้เพราะมันส่งคืนโฟลเดอร์มากเกินไป ฉันแปลงช่องว่างทั้งหมดเป็นเครื่องหมายคำถาม (อักขระตัวแทนเดียว) เช่นนี้

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /?}       # Convert every space to a question mark.
find ${SEARCH} -print

นี่คือเทียบเท่า:

find te*/my?files/more -print

สิ่งนี้ไม่เพียงส่งคืนโฟลเดอร์ที่ถูกต้องเท่านั้น แต่ยังส่งคืนterse/myxfiles/moreซึ่งไม่ควร

ฉันจะบรรลุสิ่งที่ฉันพยายามทำอย่างไร Google ไม่ได้ช่วยฉัน :(


@KasiyA ฉันใช้ทุบตี; คุณต้องใช้อย่างอื่นอย่างที่ฉันไม่เคยเห็นมาก่อน ผลลัพธ์คำสั่งSEARCH: command not foundด้วยคำสั่งที่find -printกำลังดำเนินการ
Paddy Landau

ภาพในที่มืด แต่สิ่งที่เกี่ยวกับการอ้างอิง? find "${SEARCH}" -print?
Alaa Ali

@AlaaAli ไม่ไม่ทำงานเนื่องจากการอ้างถึงป้องกัน Bash ไม่ให้ใช้สัญลักษณ์แทน มันจะมองหาเส้นทางโดยเฉพาะกับชื่อ te*/'my files'/more(ในตัวอย่างของฉัน)
Paddy Landau

คำตอบ:


9

คำสั่งเดียวกันที่แน่นอนควรทำงานได้ดีในสคริปต์:

#!/usr/bin/env bash
find  te*/my\ files/ -print

หากคุณต้องการให้มันเป็นตัวแปรมันจะซับซ้อนกว่านี้เล็กน้อย:

#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print

คำเตือน:

การใช้evalไลค์นั้นไม่ปลอดภัยและอาจส่งผลให้มีการใช้รหัสโดยอำเภอใจและอาจเป็นอันตรายหากชื่อไฟล์ของคุณมีอักขระบางตัว ดูทุบตีคำถามที่พบบ่อย 48สำหรับรายละเอียด

เป็นการดีกว่าที่จะส่งผ่านเส้นทางเป็นอาร์กิวเมนต์:

#!/usr/bin/env bash
find "$@" -name "file*"

อีกวิธีหนึ่งคือหลีกเลี่ยงการfindใช้งานร่วมกันและใช้คุณสมบัติที่กลมกล่อมและการขยายของ bash:

#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done

globstarตัวเลือกทุบตีช่วยให้คุณใช้**เพื่อให้ตรงกับซ้ำ:

globstar
      If set, the pattern ** used in a pathname expansion con
      text will match all files and zero or  more  directories
      and  subdirectories.  If the pattern is followed by a /,
      only directories and subdirectories match.

เพื่อให้มันทำหน้าที่เหมือนค้นหาและรวมไฟล์ dotfiles (ไฟล์ที่ซ่อนอยู่) 100% ให้ใช้

#!/usr/bin/env bash
shopt -s globstar
shopt -s dotglob
for file in te*/my\ files/**; do echo "$file"; done

คุณสามารถทำได้แม้กระทั่งechoโดยตรงโดยไม่ต้องวนซ้ำ:

echo te*/my\ files/**

2
ฉันได้ตั้งค่านี้เป็นคำตอบเพราะความคิดเห็นที่เป็นประโยชน์ที่ terdon ได้ทำ (ไม่ลืมความเห็นที่เป็นประโยชน์จากผู้อื่น) ฉันใช้ความสามารถในการทำให้กลมกลืนของ Bash บนบรรทัดคำสั่งเพื่อส่งผ่านหลายพา ธ ไปยังสคริปต์ของฉันแทนที่จะเป็นสคริปต์ที่พยายามแยกแยะ มันใช้งานได้ดี
Paddy Landau

2

แล้วอาร์เรย์ล่ะ?

$ tree Desktop/ Documents/
Desktop/
└── my folder
    └── more
        └── file
Documents/
└── my folder
    ├── folder
    └── more

5 directories, 1 file
$ SEARCH=(D*/my\ folder)
$ find "${SEARCH[@]}" 
Desktop/my folder
Desktop/my folder/more
Desktop/my folder/more/file
Documents/my folder
Documents/my folder/more
Documents/my folder/folder

(*)ขยายเข้าไปในอาร์เรย์ของสิ่งที่ตรงกับสัญลักษณ์แทน และ"${SEARCH[@]}"ขยายเข้าไปในองค์ประกอบทั้งหมดในอาร์เรย์ ( [@]) โดยแต่ละรายการมีการอ้างอิง

อย่างเร่งด่วนฉันรู้ว่าตัวเองควรจะสามารถนี้ สิ่งที่ต้องการ:

find . -path 'D*/my folder/more/'

ความคิดที่ฉลาด แต่ก็ไม่ได้ผล ทำไม? เนื่องจากเส้นทางนั้นถูกเก็บไว้ในตัวแปร ด้วยเหตุนี้และINPUTPATH='te*/my files/more SEARCH=(${INPUTPATH})ไม่ว่าฉันจะเปลี่ยนแปลงวิธีการทำงานของฉันอย่างไรฉันยังคงได้ผลลัพธ์ที่ไม่สามารถใช้งานได้ ดูเหมือนจะเป็นไปไม่ได้!
Paddy Landau

ทั้งหมดนี้เป็นเรื่องจริง แต่ OP ต้องดำเนินการในสคริปต์ การเปลี่ยนแปลงสิ่งต่าง ๆ เนื่องจากการขยายตัวของ wildcard นั้นมีความซับซ้อนมากขึ้นและไม่สามารถใช้งานได้
terdon

@PaddyLandau ซึ่งในกรณีนี้ทำไมคุณไม่สามารถใช้find's -pathกรอง? มันใช้สัญลักษณ์แทนและแน่นอนไม่จำเป็นต้องขยาย
muru

@muru นั่นน่าสนใจ -pathผมไม่ทราบเกี่ยวกับ อย่างไรก็ตามในช่วง 10 นาทีที่ผ่านมาฉันได้หาคำตอบแล้ว: ใช้eval! -pathมันน่าจะเป็นง่ายกว่า
Paddy Landau

2
@terdon และ muru และทุกคน: ขอบคุณ ฉันได้ยินสิ่งที่คุณพูดไปแล้วและฉันก็ตระหนักว่าฉันควรจะทำให้สคริปต์ของฉันทำสิ่งหนึ่งและให้ Bash globbing ส่งไฟล์หรือพา ธ หลาย ๆ ไฟล์ไปยังสคริปต์ ฉันได้แก้ไขสคริปต์ของฉันแล้ว มันทำงานได้ดีและเหมาะกับปรัชญา Linux ขอขอบคุณอีกครั้ง!
Paddy Landau

0

ในที่สุดฉันก็ได้พบคำตอบ

เพิ่มแบ็กสแลชไปที่ช่องว่างทั้งหมด:

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /\\ }

ณ จุดนี้มีSEARCHte*/my\ files/more

evalจากนั้นใช้

eval find ${SEARCH} -print

มันง่ายมาก! การใช้evalข้ามการตีความที่${SEARCH}มาจากตัวแปร



@terdon ขอบคุณสำหรับคำเตือน กลับไปที่กระดานวาดภาพ!
Paddy Landau

ใช่นี่มันช่างน่าประหลาดใจจริงๆ ฉันเพิ่งปรับปรุงคำตอบของฉันด้วยวิธีอื่นทำไมไม่ใช้ globbing แทน? หากยังไม่ได้ผลฉันขอแนะนำให้คุณโพสต์คำถามใหม่ในUnix & Linuxเพื่ออธิบายว่าเป้าหมายสุดท้ายของคุณคืออะไรและทำไมคุณต้องมีรูปแบบเป็นตัวแปร สิ่งประเภทนี้น่าจะได้คำตอบที่ดีกว่า
terdon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.