Làm cách nào để truyền từng dòng của tệp văn bản làm đối số cho lệnh?


42

Tôi đang tìm cách viết một tập lệnh lấy .txttên tệp làm đối số, đọc từng dòng tệp và chuyển từng dòng cho một lệnh. Ví dụ, nó chạy command --option "LINE 1", sau đó command --option "LINE 2", vv Đầu ra của lệnh được ghi vào một tệp khác. Làm thế nào để tôi đi về làm điều đó? Tôi không biết bắt đầu từ đâu.

Câu trả lời:


26

Sử dụng while readvòng lặp:

: > another_file  ## Truncate file.

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

Một cách khác là chuyển hướng đầu ra theo khối:

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

Cuối cùng là mở tệp:

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

Nếu một trong các lệnh đọc đầu vào, đó là một ý tưởng tốt để sử dụng một fd khác cho đầu vào để các lệnh sẽ không ăn nó (ở đây giả sử ksh, zshhoặc bashcho -u 3, sử dụng <&3thay vì có thể di chuyển):

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

Cuối cùng để chấp nhận đối số, bạn có thể làm:

#!/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"

Cái nào có thể chạy như:

bash script.sh file another_file

Thêm ý tưởng. Với bash, sử dụng readarray:

readarray -t LINES < "$FILE"

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

Lưu ý: IFS=có thể được bỏ qua nếu bạn không nhớ có các giá trị dòng được cắt bớt các khoảng trắng ở đầu và cuối.


28

Một lựa chọn khác là xargs.

Với GNU xargs:

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

{} là nơi giữ cho dòng văn bản.

Khác xargskhông có -d, nhưng một số có -0đầu vào được phân định bằng NUL. Với những thứ đó, bạn có thể làm:

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

Trên các hệ thống tuân thủ Unix ( -Ilà tùy chọn trong POSIX và chỉ bắt buộc đối với các hệ thống tuân thủ Unix), bạn cần xử lý trước đầu vào để trích dẫn các dòng theo định dạng được mong đợi bởi xargs:

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

Tuy nhiên, lưu ý rằng một số xargstriển khai có giới hạn rất thấp đối với kích thước tối đa của đối số (ví dụ 255 trên Solaris, mức tối thiểu được cho phép bởi đặc tả Unix).


có thể giảm xuống<file xargs -L 1 -I{} command --option {} other args
iruvar

@iruvar, không, sẽ xử lý dấu ngoặc kép và xóa một số khoảng trống.
Stéphane Chazelas

7

Giữ chính xác cho câu hỏi:

#!/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
làm việc như bùa mê :-)
BugHunter

3

Câu trả lời tốt nhất tôi tìm thấy là:

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

BIÊN TẬP:

... bốn năm sau ...

sau vài lần bỏ phiếu và thêm một số kinh nghiệm tôi muốn giới thiệu ngay sau đây

xargs -l COMMAND < file

3
Điều này sẽ gọi lệnh một lần cho mỗi từ trong tệp. Ngoài ra, bạn phải luôn trích dẫn các tham chiếu đến các biến shell (như trong do "$cmd" "$i";) trừ khi bạn có lý do để không; nếu tệp chứa một từ *như chính nó, mã của bạn sẽ chạy $cmd *, tất nhiên, sẽ chạy lệnh với một danh sách các tệp trong thư mục hiện tại.
G-Man nói 'Phục hồi Monica'

1
@ G-Man, ngoại trừ zsh, `cat`sẽ mở rộng *(không được trích dẫn $ivẫn có thể mở rộng một số ký tự đại diện (vòng thứ hai) nếu mở rộng `cat`giới thiệu một số). Trong mọi trường hợp, cách tiếp cận đó là thực sự sai.
Stéphane Chazelas

1
+1. Điều này sẽ hoạt động tốt, nếu mỗi dòng chỉ chứa đầu vào cần thiết cho lệnh đang được sử dụng mà không có khoảng trắng.
Subin Sebastian

Điều này hành động trên mỗi từ, có một biến thể của điều này hoạt động trên mỗi dòng?
thebunnyrules

Câu hỏi là xử lý một danh sách các tên tệp theo từng dòng.
Con lăn Steffen

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

ĐẦU RA

--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

Lấy tất cả các dòng của một tệp và chuyển chúng dưới dạng đối số cho một lệnh, nghĩa là,

command line1 line2 line3 ....

Nếu bạn cần --optioncờ đứng trước mỗi dòng, hãy thay đổi lệnh thứ hai thành:

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

1
-1 để sử dụng ed ... thực sự là gì?
dùng2217522

3
Tôi đã không đánh giá thấp bạn, nhưng người có lẽ có những lý do sau: (1) Điều này không làm những gì câu hỏi yêu cầu. Điều này gọi lệnh một lần với tất cả các nội dung của tệp trên dòng lệnh; thay vì một lần trên mỗi dòng . (2) Điều này không có gì để xử lý các ký tự đặc biệt đối với trình bao (có thể có trong tệp); ví dụ ', ", <, >, ;, vv (3) Điều này tạo ra một tập tin tạm thời không cần thiết. (4) Những việc như thế này thường được thực hiện với các tài liệu ở đây. (5) edLệnh của bạn vụng về; hai lệnh đầu tiên có thể được giảm xuống %s/^/ /%j.
G-Man nói 'Phục hồi Monica'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.