ตัวอย่างเช่นตอนนี้ฉันใช้สิ่งต่อไปนี้เพื่อเปลี่ยนไฟล์ที่มีพา ธ Unix ที่ฉันเขียนไปเป็นไฟล์:
cat file.txt | while read in; do chmod 755 "$in"; done
มีวิธีที่สง่างามและปลอดภัยกว่านี้ไหม?
ตัวอย่างเช่นตอนนี้ฉันใช้สิ่งต่อไปนี้เพื่อเปลี่ยนไฟล์ที่มีพา ธ Unix ที่ฉันเขียนไปเป็นไฟล์:
cat file.txt | while read in; do chmod 755 "$in"; done
มีวิธีที่สง่างามและปลอดภัยกว่านี้ไหม?
คำตอบ:
นี่เป็นเพราะมีเพียง 1 คำตอบเท่านั้น ...
shell
การขยายบรรทัดคำสั่งxargs
เครื่องมือเฉพาะwhile read
ด้วยข้อสังเกตบางอย่างwhile read -u
ใช้เฉพาะfd
สำหรับการประมวลผลแบบโต้ตอบ (ตัวอย่าง)เกี่ยวกับคำขอ OP นี้: การทำงานchmod
กับเป้าหมายทั้งหมดที่ระบุไว้ในแฟ้ม , xargs
เป็นเครื่องมือที่ระบุ แต่สำหรับแอปพลิเคชั่นอื่น ๆ ไฟล์จำนวนเล็กน้อย ฯลฯ ...
หากไฟล์ของคุณไม่ใหญ่เกินไปและไฟล์ทั้งหมดมีชื่อที่ดี (ไม่มีช่องว่างหรือตัวอักษรพิเศษอื่น ๆ เช่นเครื่องหมายคำพูด) คุณสามารถใช้shell
การขยายบรรทัดคำสั่งได้ เพียง:
chmod 755 $(<file.txt)
สำหรับไฟล์จำนวนเล็กน้อย (บรรทัด) คำสั่งนี้จะเบากว่า
xargs
เป็นเครื่องมือที่เหมาะสมสำหรับไฟล์ที่มีจำนวนมากขึ้นหรือเกือบทุกบรรทัดในไฟล์อินพุตของคุณ ...
สำหรับหลาย ๆ คนbinutilsเครื่องมือเช่นchown
, chmod
, rm
, cp -t
...
xargs chmod 755 <file.txt
ถ้าคุณมีตัวอักษรพิเศษและ / file.txt
หรือจำนวนมากของสายใน
xargs -0 chmod 755 < <(tr \\n \\0 <file.txt)
หากคำสั่งของคุณจำเป็นต้องเรียกใช้ 1 ครั้งโดยรายการ:
xargs -0 -n 1 chmod 755 < <(tr \\n \\0 <file.txt)
สิ่งนี้ไม่จำเป็นสำหรับตัวอย่างนี้เนื่องจากchmod
ยอมรับหลายไฟล์เป็นอาร์กิวเมนต์ แต่ตรงกับหัวเรื่องของคำถาม
สำหรับกรณีพิเศษบางอย่างคุณสามารถกำหนดตำแหน่งของอาร์กิวเมนต์ไฟล์ในคำสั่งที่สร้างโดยxargs
:
xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \\n \\0 <file.txt)
seq 1 5
อินพุตลองสิ่งนี้:
xargs -n 1 -I{} echo Blah {} blabla {}.. < <(seq 1 5)
Blah 1 blabla 1..
Blah 2 blabla 2..
Blah 3 blabla 3..
Blah 4 blabla 4..
Blah 5 blabla 5..
ที่ไหน Commande จะทำครั้งเดียวต่อบรรทัด
while read
และตัวแปรตามที่ OP แนะนำcat file.txt | while read in; do chmod 755 "$in"; done
จะใช้งานได้ แต่มี 2 ประเด็น:
cat |
เป็นทางแยกที่ไร้ประโยชน์และ
| while ... ;done
จะกลายเป็นsubshell;done
ที่สภาพแวดล้อมจะหายไปเช่นกันหลังจากที่
ดังนั้นนี่อาจเป็นลายลักษณ์อักษรที่ดีกว่า:
while read in; do chmod 755 "$in"; done < file.txt
แต่,
คุณอาจได้รับคำเตือนเกี่ยวกับ$IFS
และread
ธง:
help read
read: read [-r] ... [-d delim] ... [name ...] ... Reads a single line from the standard input... The line is split into fields as with word splitting, and the first word is assigned to the first NAME, the second word to the second NAME, and so on... Only the characters found in $IFS are recognized as word delimiters. ... Options: ... -d delim continue until the first character of DELIM is read, rather than newline ... -r do not allow backslashes to escape any characters ... Exit Status: The return code is zero, unless end-of-file is encountered...
ในบางกรณีคุณอาจจำเป็นต้องใช้
while IFS= read -r in;do chmod 755 "$in";done <file.txt
สำหรับการหลีกเลี่ยงปัญหากับชื่อไฟล์ที่หลากหลาย และถ้าคุณเข้ารหัสปัญหาด้วยUTF-8
:
while LANG=C IFS= read -r in ; do chmod 755 "$in";done <file.txt
ในขณะที่คุณใช้STDIN
สำหรับการอ่านfile.txt
สคริปต์ของคุณไม่สามารถโต้ตอบได้ (คุณไม่สามารถใช้STDIN
อีกต่อไป)
while read -u
fd
โดยใช้ทุ่มเทไวยากรณ์: while read ...;done <file.txt
จะเปลี่ยนเส้นทางไปยังSTDIN
file.txt
หมายความว่าคุณจะไม่สามารถจัดการกับกระบวนการจนกว่าจะเสร็จสิ้น
หากคุณวางแผนที่จะสร้างเครื่องมือแบบโต้ตอบคุณต้องหลีกเลี่ยงการใช้STDIN
และใช้ตัวอธิบายไฟล์อื่นๆ
คงอธิบายไฟล์คือ: 0
สำหรับSTDIN , 1
สำหรับSTDOUTและ2
สำหรับSTDERR คุณสามารถดูได้โดย:
ls -l /dev/fd/
หรือ
ls -l /proc/self/fd/
จากตรงนั้นคุณต้องเลือกหมายเลขที่ไม่ได้ใช้ระหว่าง0
และ63
(ขึ้นอยู่กับความเป็นจริงโดยขึ้นอยู่กับsysctl
เครื่องมือ superuser) เป็นตัวบอกไฟล์ :
สำหรับการสาธิตนี้ฉันจะใช้fd 7
:
exec 7<file.txt # Without spaces between `7` and `<`!
ls -l /dev/fd/
จากนั้นคุณสามารถใช้read -u 7
วิธีนี้:
while read -u 7 filename;do
ans=;while [ -z "$ans" ];do
read -p "Process file '$filename' (y/n)? " -sn1 foo
[ "$foo" ]&& [ -z "${foo/[yn]}" ]&& ans=$foo || echo '??'
done
if [ "$ans" = "y" ] ;then
echo Yes
echo "Processing '$filename'."
else
echo No
fi
done 7<file.txt
done
เพื่อปิดfd/7
:
exec 7<&- # This will close file descriptor 7.
ls -l /dev/fd/
หมายเหตุ: ฉันปล่อยรุ่นที่มีขีดกลางเนื่องจากไวยากรณ์นี้อาจเป็นประโยชน์เมื่อทำ I / O จำนวนมากด้วยกระบวนการที่คล้ายคลึงกัน:
mkfifo sshfifo
exec 7> >(ssh -t user@host sh >sshfifo)
exec 6<sshfifo
cat file.txt | tr \\n \\0 | xargs -0 -n1 chmod 755
tr \\n \\0 <file.txt |xargs -0 [command]
เร็วกว่าวิธีที่คุณอธิบายประมาณ 50%
ใช่.
while read in; do chmod 755 "$in"; done < file.txt
วิธีนี้คุณสามารถหลีกเลี่ยงcat
กระบวนการ
cat
เกือบจะไม่ดีเสมอไปสำหรับวัตถุประสงค์เช่นนี้ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับการใช้แมวที่ไร้ประโยชน์
cat
เป็นความคิดที่ดี แต่ในกรณีนี้คำสั่งที่ระบุเป็นxargs
chmod
(เช่นเรียกใช้คำสั่งเดียวสำหรับแต่ละบรรทัดในไฟล์)
read -r
อ่านบรรทัดเดียวจากอินพุตมาตรฐาน ( read
โดยไม่-r
แปลเครื่องหมายแบ็กสแลชคุณไม่ต้องการ)"
หากคุณมีตัวเลือกที่ดี (เช่นไฟล์. txt ทั้งหมดใน dir) คุณสามารถทำได้:
for i in *.txt; do chmod 755 "$i"; done
หรือตัวแปรของคุณ:
while read line; do chmod 755 "$line"; done <file.txt
หากคุณรู้ว่าคุณไม่มีช่องว่างในอินพุต:
xargs chmod 755 < file.txt
หากอาจมีช่องว่างในเส้นทางและถ้าคุณมี GNU xargs:
tr '\n' '\0' < file.txt | xargs -0 chmod 755
xargs
แข็งแกร่ง เครื่องมือนี้เก่ามากและรหัสของเขาขอมาเยือน เป้าหมายของเขาคือการสร้างบรรทัดในส่วนที่เกี่ยวกับข้อ จำกัด ของเชลล์ (64kchar / บรรทัดหรือบางอย่าง) ตอนนี้เครื่องมือนี้สามารถทำงานกับไฟล์ที่มีขนาดใหญ่มากและอาจลดจำนวนส้อมลงเป็นคำสั่งสุดท้าย ดูคำตอบและ / หรือของman xargs
ฉัน
brew install findutils
) จากนั้นเรียกใช้ GNU xargs ด้วยgxargs
แทนเช่นgxargs chmod 755 < file.txt
หากคุณต้องการรันคำสั่งแบบขนานสำหรับแต่ละบรรทัดคุณสามารถใช้GNU Parallel ได้
parallel -a <your file> <program>
แต่ละบรรทัดของไฟล์ของคุณจะถูกส่งผ่านไปยังโปรแกรมเป็นอาร์กิวเมนต์ โดยค่าเริ่มต้นparallel
จะรันเธรดให้มากที่สุดเท่าที่ CPU ของคุณจะนับ แต่คุณสามารถระบุด้วย-j
ฉันเห็นว่าคุณติดแท็ก bash แต่ Perl ก็จะเป็นวิธีที่ดีในการทำเช่นนี้:
perl -p -e '`chmod 755 $_`' file.txt
คุณสามารถใช้ regex เพื่อให้แน่ใจว่าคุณได้รับไฟล์ที่ถูกต้องเช่นกับการประมวลผลไฟล์. txt เท่านั้น:
perl -p -e 'if(/\.txt$/) `chmod 755 $_`' file.txt
หากต้องการ "ดูตัวอย่าง" สิ่งที่เกิดขึ้นเพียงแทนที่ backticks ด้วยเครื่องหมายคำพูดคู่และเพิ่มprint
:
perl -p -e 'if(/\.txt$/) print "chmod 755 $_"' file.txt
chmod
ฟังก์ชั่น
perl -lpe 'chmod 0755, $_' file.txt
- ใช้-l
สำหรับคุณสมบัติ "auto-chomp"
นอกจากนี้คุณยังสามารถใช้ AWK ซึ่งสามารถเพิ่มความยืดหยุ่นในการจัดการไฟล์
awk '{ print "chmod 755 "$0"" | "/bin/sh"}' file.txt
หากไฟล์ของคุณมีตัวคั่นฟิลด์เช่น:
field1, field2, Field3
เพื่อรับเฉพาะฟิลด์แรกที่คุณทำ
awk -F, '{ print "chmod 755 "$1"" | "/bin/sh"}' file.txt
คุณสามารถตรวจสอบรายละเอียดเพิ่มเติมเกี่ยวกับเอกสาร GNU ได้ที่ https://www.gnu.org/software/gawk/manual/html_node/Very-Simple.html#Very-Simple
ตรรกะนี้ใช้กับวัตถุประสงค์อื่น ๆ อีกมากมาย และวิธีการอ่าน. sh_history ของผู้ใช้แต่ละคนจาก / home / filesystem? เกิดอะไรขึ้นถ้ามีหลายพันคน?
#!/bin/ksh
last |head -10|awk '{print $1}'|
while IFS= read -r line
do
su - "$line" -c 'tail .sh_history'
done
นี่คือสคริปต์https://github.com/imvieira/SysAdmin_DevOps_Scripts/blob/master/get_and_run.sh
ฉันรู้ว่ามันสาย แต่ก็ยัง
หากมีโอกาสที่คุณพบไฟล์ข้อความที่บันทึกด้วย windows \r\n
แทนที่จะเป็น\n
คุณอาจสับสนโดยเอาต์พุตถ้าคำสั่งของคุณมี sth หลังจากอ่านบรรทัดเป็นอาร์กิวเมนต์ ดังนั้นจะลบ\r
ตัวอย่างเช่น:
cat file | tr -d '\r' | xargs -L 1 -i echo do_sth_with_{}_as_line
xargs
เป็น Initialy สร้างสำหรับการตอบรับชนิดของความต้องการคุณสมบัติบางอย่างนี้เช่นการสร้างคำสั่งเป็นเวลานานที่สุดในสภาพแวดล้อมปัจจุบันสำหรับการเรียกchmod
ในกรณีนี้เป็นไปได้น้อยลงเช่นลดส้อมให้มั่นใจ efficiencewhile ;do..done <$file
implie กำลังเรียกใช้ 1 fork สำหรับ 1 ไฟล์xargs
สามารถรัน 1 fork สำหรับไฟล์พันไฟล์ ... ในลักษณะที่เชื่อถือได้