วิธีที่สอดคล้องกับ POSIX ในการทำงานกับรายการชื่อไฟล์ที่อาจมีช่องว่าง


14

ฉันได้เห็นคำแนะนำในการเขียนสคริปต์ของ Bash แนะนำให้ใช้อาร์เรย์สำหรับทำงานกับชื่อไฟล์ที่มีช่องว่าง แต่DashAsBinShชี้ให้เห็นว่าอาร์เรย์ไม่สามารถเคลื่อนย้ายได้ดังนั้นฉันกำลังมองหาวิธีการทำงานร่วมกับ POSIX ในการทำงานกับรายการชื่อไฟล์ที่อาจมีช่องว่าง

ฉันต้องการแก้ไขสคริปต์ตัวอย่างด้านล่างเพื่อที่จะได้ echo

foo/target/a.jar
foo/target/b.jar
bar/target/lol whitespace.jar

นี่คือสคริปต์

#!/usr/bin/env sh

INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"
# this would be produced by a 'ls' command
# We can execute the ls within the script, if it helps

dostuffwith() { echo $1; };

F_LOCATIONS=$INPUT
ALL_FILES=$(for f in $F_LOCATIONS; do echo `basename $f`; done)
ALL_FILES=$(echo "$ALL_FILES" | sort | uniq)

for f in $ALL_FILES
do
    fpath=$(echo "$F_LOCATIONS" | grep -m1 $f)
    dostuffwith $fpath
done

เป็นไปได้เช่นเดียวกันกับ SO: stackoverflow.com/questions/6499486/…
Ciro Santilli 事件改造中心中心法轮功六四

คำตอบ:


8

เปลือกหอย POSIX มีหนึ่งอาร์เรย์: พารามิเตอร์ตำแหน่ง ( $1, $2ฯลฯ รวมอ้างว่า"$@")

set -- 'foo/target/a.jar' 'foo/target/b.jar' 'bar/target/b.jar' 'bar/target/lol whitespace.jar'
set -- "$@" '/another/one at the end.jar'

for jar do
  dostuffwith "$jar"
done

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

หากชื่อไฟล์ของคุณรับประกันว่าจะไม่มีบรรทัดใหม่คุณสามารถใช้บรรทัดใหม่เป็นตัวคั่นได้ เมื่อคุณขยายตัวแปรขั้นแรกให้ปิดการวนรอบด้วยset -fและตั้งค่ารายการอักขระการแยกฟิลด์IFSเพื่อให้มีบรรทัดใหม่เท่านั้น

INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"

set -f; IFS='
'                           # turn off variable value expansion except for splitting at newlines
for jar in $INPUT; do
  set +f; unset IFS
  dostuffwith "$jar"        # restore globbing and field splitting at all whitespace
done
set +f; unset IFS           # do it again in case $INPUT was empty

sortกับรายการของคุณในรายการคั่นด้วยการขึ้นบรรทัดใหม่คุณสามารถใช้คำสั่งประมวลผลข้อความจำนวนมากเป็นประโยชน์โดยเฉพาะอย่างยิ่ง

อย่าลืมใส่เครื่องหมายคำพูดคู่ล้อมรอบการแทนที่ตัวแปรเสมอยกเว้นเมื่อคุณต้องการให้การแบ่งฟิลด์เกิดขึ้นอย่างชัดเจน


คำตอบและคำอธิบายที่ดี ฉันจะทำเครื่องหมายว่าเป็นที่ยอมรับเพราะทำให้sort | uniqขั้นตอนเดิมเป็นไปตามที่ตั้งใจไว้
Eero Aaltonen

5

เนื่องจาก$INPUTตัวแปรของคุณใช้การขึ้นบรรทัดใหม่เป็นตัวคั่นฉันจะสมมติว่าไฟล์ของคุณจะไม่มีการขึ้นบรรทัดใหม่ในชื่อ เช่นนั้นใช่มีวิธีง่ายๆในการวนซ้ำไฟล์และรักษาพื้นที่ว่าง

ความคิดคือการใช้readเปลือก builtin โดยปกติแล้วreadจะแบ่งช่องว่างใด ๆ และช่องว่างจะแตก แต่คุณสามารถตั้งค่าIFS=$'\n'และมันจะแยกขึ้นบรรทัดใหม่เท่านั้น ดังนั้นคุณสามารถทำซ้ำแต่ละบรรทัดในรายการของคุณ

นี่เป็นทางออกที่เล็กที่สุดที่ฉันจะได้รับ:

INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"

dostuffwith() {
    echo "$1"
}

echo "$INPUT" | awk -F/ '{if (!seen[$NF]++) print }' | \
while IFS=$'\n' read file; do
  dostuffwith "$file"
done

โดยพื้นฐานแล้วมันจะส่ง "$ INPUT" awkซึ่งเป็นไฟล์ที่ซ้ำซ้อนตามชื่อไฟล์/แล้วพิมพ์บรรทัดหากรายการสุดท้ายไม่เคยเห็นมาก่อน) จากนั้นเมื่อ awk สร้างรายการพา ธ ไฟล์เราจะใช้วนwhile readซ้ำตามรายการ


$ checkbashisms bar.sh เป็นไปได้ bashism ใน bar.sh บรรทัดที่ 14 (<<< นี่คือสตริง)
Eero Aaltonen

1
@EeroAaltonen เปลี่ยนเป็นไม่ใช้ herestring โปรดทราบว่าด้วยการเปลี่ยนแปลงนี้whileลูปและดังนั้นdostuffwithจะถูกดำเนินการใน subshell ดังนั้นตัวแปรหรือการเปลี่ยนแปลงใด ๆ ที่เกิดขึ้นกับเชลล์ที่รันอยู่จะหายไปเมื่อลูปเสร็จสมบูรณ์ ทางเลือกเดียวคือใช้ heredoc เต็มรูปแบบซึ่งไม่ได้เป็นที่น่ารังเกียจ แต่ฉันคิดว่ามันจะดีกว่า
แพทริค

ฉันให้คะแนนตามความสามารถในการอ่านมากกว่าความเล็ก สิ่งนี้ใช้ได้จริงและ +1 สำหรับสิ่งนั้นแล้ว
Eero Aaltonen

IFS="\n"แยกแบ็กสแลชและอักขระ n แต่read fileไม่มีการแยก IFS="\n"ยังคงมีประโยชน์ในการที่จะลบอักขระว่างจาก $ IFS ซึ่งมิฉะนั้นจะถูกปล้นที่จุดเริ่มต้นและจุดสิ้นสุดของอินพุต หากต้องการอ่านบรรทัดไวยากรณ์ของแคนนอนคือIFS= read -r lineแม้ว่าIFS=anything read -r line(หากมีสิ่งใดที่ไม่มีช่องว่าง) ก็ใช้งานได้เช่นกัน
Stéphane Chazelas

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