วิธีส่งแต่ละบรรทัดของไฟล์ข้อความเป็นอาร์กิวเมนต์ไปยังคำสั่ง?


42

ฉันต้องการเขียนสคริปต์ที่ใช้.txtชื่อไฟล์เป็นอาร์กิวเมนต์อ่านไฟล์ทีละบรรทัดและส่งแต่ละบรรทัดไปยังคำสั่ง ตัวอย่างเช่นมันทำงานcommand --option "LINE 1"แล้วcommand --option "LINE 2"ฯลฯ เอาท์พุทของคำสั่งจะถูกเขียนไปยังไฟล์อื่น ฉันจะทำเช่นนั้นได้อย่างไร ฉันไม่รู้ว่าจะเริ่มจากตรงไหน

คำตอบ:


26

ใช้while readวง:

: > another_file  ## Truncate file.

while IFS= read -r LINE; do
    command --option "$LINE" >> another_file
done < file

อีกวิธีหนึ่งคือเปลี่ยนเส้นทางผลลัพธ์โดยบล็อก:

while IFS= read -r LINE; do
    command --option "$LINE"
done < file > another_file

สุดท้ายคือการเปิดไฟล์:

exec 4> another_file

while IFS= read -r LINE; do
    command --option "$LINE" >&4
    echo xyz  ## Another optional command that sends output to stdout.
done < file

หากหนึ่งในคำสั่งอ่านอินพุตมันจะเป็นความคิดที่ดีที่จะใช้ FD อีกสำหรับการป้อนข้อมูลเพื่อให้คำสั่งที่จะไม่กินมัน (ที่นี่สมมติksh, zshหรือbashสำหรับ-u 3ใช้<&3แทน portably):

while IFS= read -ru 3 LINE; do
    ...
done 3< file

ในที่สุดก็ยอมรับข้อโต้แย้งคุณสามารถทำได้:

#!/bin/bash

FILE=$1
ANOTHER_FILE=$2

exec 4> "$ANOTHER_FILE"

while IFS= read -ru 3 LINE; do
    command --option "$LINE" >&4
done 3< "$FILE"

สิ่งที่สามารถเรียกใช้เป็น:

bash script.sh file another_file

แนวคิดเพิ่มเติม ด้วยการbashใช้readarray:

readarray -t LINES < "$FILE"

for LINE in "${LINES[@]}"; do
    ...
done

หมายเหตุ: IFS=สามารถตัดออกได้หากคุณไม่สนใจว่าจะต้องตัดค่าบรรทัดของช่องว่างนำหน้าและส่วนท้าย


28

xargsตัวเลือกหนึ่งคือ

ด้วย GNU xargs:

< file xargs -I{} -d'\n' command --option {} other args

{} เป็นตัวยึดตำแหน่งของบรรทัดข้อความ

อื่น ๆxargsไม่มี-dแต่บาง-0ตัวมีอินพุต NUL คั่นด้วย ด้วยสิ่งเหล่านี้คุณสามารถทำได้:

< file tr '\n' '\0' | xargs -0 -I{} command --option {} other args

บนระบบที่สอดคล้องกับ Unix ( -Iเป็นทางเลือกใน POSIX และจำเป็นสำหรับระบบที่สอดคล้องกับ Unix เท่านั้น) คุณจะต้องประมวลผลอินพุตล่วงหน้าเพื่อเสนอราคาบรรทัดในรูปแบบที่คาดหวังโดยxargs:

< file sed 's/"/"\\""/g;s/.*/"&"/' |
  xargs -E '' -I{} command --option {} other args

อย่างไรก็ตามโปรดทราบว่าxargsการใช้งานบางอย่างมีขีด จำกัด ต่ำมากเกี่ยวกับขนาดสูงสุดของการโต้แย้ง (255 บน Solaris เช่นขั้นต่ำที่ได้รับอนุญาตโดยข้อกำหนด Unix)


ลดให้เป็น<file xargs -L 1 -I{} command --option {} other args
iruvar

@iruvar ไม่ว่าจะประมวลผลคำพูดและลบช่องว่างออก
Stéphane Chazelas

7

การรักษาคำถามให้แม่นยำ:

#!/bin/bash

# xargs -n param sets how many lines from the input to send to the command

# Call command once per line
[[ -f $1 ]] && cat $1 | xargs -n1 command --option

# Call command with 2 lines as args, such as an openvpn password file
# [[ -f $1 ]] && cat $1 | xargs -n2 command --option

# Call command with all lines as args
# [[ -f $1 ]] && cat $1 | xargs command --option

1
ทำงานได้อย่างมีเสน่ห์ :-)
BugHunter

3

คำตอบที่ดีที่สุดที่ฉันพบคือ:

for i in `cat`; do "$cmd" "$i"; done < $file

แก้ไข:

... สี่ปีต่อมา ...

หลังจากลงคะแนนเสียงหลายครั้งและมีประสบการณ์มากขึ้นฉันจะแนะนำต่อไปนี้

xargs -l COMMAND < file

3
สิ่งนี้จะเรียกคำสั่งหนึ่งคำสำหรับแต่ละคำในไฟล์ นอกจากนี้คุณควรอ้างอิงการอ้างอิงไปยังตัวแปรเชลล์ (เหมือนในdo "$cmd" "$i";) เว้นแต่คุณจะมีเหตุผลที่จะไม่; หากไฟล์มี*คำศัพท์ด้วยตัวเองโค้ดของคุณจะทำงาน$cmd *ซึ่งแน่นอนว่าจะรันคำสั่งด้วยรายการไฟล์ในไดเรกทอรีปัจจุบัน
G-Man กล่าวว่า 'Reinstate Monica'

1
@ G-Man, ยกเว้นในzsh, `cat`จะขยายแล้ว*(ผู้ที่$iไม่สามารถอ้างอิงยังคงสามารถขยายไวด์การ์ดบางส่วน (รอบที่สอง) หากการขยายตัวของการ`cat`แนะนำบางส่วน) ไม่ว่าในกรณีใดวิธีการนั้นจะผิดแน่นอน
Stéphane Chazelas

1
+1 สิ่งนี้จะทำงานได้ดีถ้าแต่ละบรรทัดมีเฉพาะอินพุตที่จำเป็นสำหรับคำสั่งที่ใช้โดยไม่มีช่องว่าง
Subin Sebastian

สิ่งนี้ทำหน้าที่ในแต่ละคำมีความแตกต่างของสิ่งนี้ที่ทำหน้าที่ในแต่ละบรรทัดหรือไม่?
thebunnyrules

คำถามคือการประมวลผลรายการชื่อไฟล์ทีละบรรทัด
Steffen Roller

2
    sed "s/'/'\\\\''/g;s/.*/\$* '&'/" <<\FILE |\
    sh -s -- command echo --option
all of the{&}se li$n\es 'are safely shell
quoted and handed to command as its last argument
following --option, and, here, before that echo
FILE

เอาท์พุท

--option all of the{&}se li$n\es 'are safely shell
--option quoted and handed to command as its last argument
--option following --option, and, here, before that echo

-2
ed file.txt
%g/^/s// /
2,$g/^/-,.j
1s/^/command/
wq
chmod 755 file.txt
./file.txt

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

command line1 line2 line3 ....

หากคุณต้องการให้--optionแฟล็กนำหน้าแต่ละบรรทัดเปลี่ยนคำสั่งที่สองเป็น:

%g/^/s// --option /

1
สิ่งที่ -1 สำหรับการใช้ ed ... จริงเหรอ?
user2217522

3
ฉันไม่ได้ลงคะแนนให้คุณ แต่คนที่อาจมีเหตุผลเหล่านี้: (1) สิ่งนี้ไม่ได้ทำในสิ่งที่คำถามถาม สิ่งนี้จะเรียกใช้คำสั่งเพียงครั้งเดียวด้วยเนื้อหาทั้งหมดของไฟล์บนบรรทัดคำสั่ง มากกว่าหนึ่งครั้งต่อบรรทัด (2) สิ่งนี้ไม่ทำอะไรเลยเพื่อจัดการกับอักขระที่พิเศษสำหรับเชลล์ (อาจอยู่ในไฟล์) เช่น', ", <, >, ;และอื่น ๆ (3) นี้จะสร้างแฟ้มชั่วคราวที่ไม่จำเป็น (4) สิ่งนี้มักทำด้วย“ เอกสารที่นี่” (5) edคำสั่งของคุณเงอะงะ ทั้งสองคำสั่งแรกที่สามารถลดลงไปและ%s/^/ / %j
G-Man กล่าวว่า 'Reinstate Monica'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.