Nói, tôi có một tệp foo.txt
chỉ định N
đối số
arg1
arg2
...
argN
mà tôi cần phải truyền cho lệnh my_command
Làm cách nào để sử dụng các dòng của tệp làm đối số của lệnh?
Nói, tôi có một tệp foo.txt
chỉ định N
đối số
arg1
arg2
...
argN
mà tôi cần phải truyền cho lệnh my_command
Làm cách nào để sử dụng các dòng của tệp làm đối số của lệnh?
Câu trả lời:
Nếu shell của bạn là bash (trong số những cái khác), một phím tắt $(cat afile)
là $(< afile)
, vì vậy bạn sẽ viết:
mycommand "$(< file.txt)"
Tài liệu trong trang bash man trong phần 'Lệnh thay thế'.
Thay vào đó, có lệnh của bạn đọc từ stdin, vì vậy: mycommand < file.txt
<
toán tử. Điều đó có nghĩa là chính shell sẽ thực hiện chuyển hướng thay vì thực hiện cat
nhị phân cho các thuộc tính chuyển hướng của nó.
"$(…)"
, nội dung của file.txt
sẽ được chuyển đến đầu vào tiêu chuẩn của mycommand
, không phải là một đối số. "$(…)"
có nghĩa là chạy lệnh và trả lại đầu ra dưới dạng một chuỗi ; Ở đây, lệnh Lệnh chỉ đọc các tập tin nhưng nó có thể phức tạp hơn.
Như đã đề cập, bạn có thể sử dụng backticks hoặc $(cat filename)
.
Điều không được đề cập và tôi nghĩ điều quan trọng cần lưu ý là bạn phải nhớ rằng trình bao sẽ phân tách nội dung của tệp đó theo khoảng trắng, đưa ra từng "từ" mà nó tìm thấy cho lệnh của bạn làm đối số. Và mặc dù bạn có thể kèm theo một đối số dòng lệnh trong dấu ngoặc kép để nó có thể chứa khoảng trắng, các chuỗi thoát, v.v., việc đọc từ tệp sẽ không làm điều tương tự. Ví dụ: nếu tệp của bạn chứa:
a "b c" d
các đối số bạn sẽ nhận được là:
a
"b
c"
d
Nếu bạn muốn kéo từng dòng làm đối số, hãy sử dụng cấu trúc while / read / do:
while read i ; do command_name $i ; done < filename
read -r
trừ khi bạn muốn mở rộng các chuỗi thoát dấu gạch chéo ngược - và NUL là một dấu phân cách an toàn hơn để sử dụng so với dòng mới, đặc biệt nếu các đối số bạn chuyển qua là những thứ như tên tệp, có thể chứa các dòng mới. Ngoài ra, không xóa IFS, bạn sẽ bị xóa khoảng trắng hàng đầu và dấu vết i
.
command `< file`
sẽ chuyển nội dung tệp cho lệnh trên stdin, nhưng sẽ loại bỏ các dòng mới, nghĩa là bạn không thể lặp lại từng dòng riêng lẻ. Vì vậy, bạn có thể viết một tập lệnh với vòng lặp 'for':
for line in `cat input_file`; do some_command "$line"; done
Hoặc (biến thể đa dòng):
for line in `cat input_file`
do
some_command "$line"
done
Hoặc (biến thể nhiều dòng $()
thay vì ``
):
for line in $(cat input_file)
do
some_command "$line"
done
< file
backticks có nghĩa là nó thực sự không thực hiện chuyển hướng cho command
tất cả.
command `< file`
Không hoạt động trong bash hoặc POSIX sh; zsh là một vấn đề khác, nhưng đó không phải là vấn đề mà câu hỏi này đề cập đến).
Hello World
trên một dòng. Bạn sẽ thấy nó chạy đầu tiên some_command Hello
, sau đó some_command World
, không some_command "Hello World"
hoặc some_command Hello World
.
Bạn làm điều đó bằng cách sử dụng backticks:
echo World > file.txt
echo Hello `cat file.txt`
echo "Hello * Starry * World" > file.txt
ở bước đầu tiên, bạn sẽ nhận được ít nhất bốn đối số riêng biệt được truyền cho lệnh thứ hai - và có khả năng hơn nữa, vì *
s sẽ mở rộng thành tên của các tệp có trong thư mục hiện tại.
fork()
lấy ra một lớp con với một phần tử gắn liền với thiết bị xuất chuẩn của nó, sau đó gọi /bin/cat
là một đứa con của lớp con đó, sau đó đọc kết quả đầu ra thông qua FIFO; so sánh với $(<file.txt)
, trong đó đọc nội dung của tệp thành bash trực tiếp mà không có subshells hoặc FIFO liên quan.
Nếu bạn muốn thực hiện điều này một cách mạnh mẽ, hoạt động cho mọi đối số dòng lệnh có thể (giá trị có khoảng trắng, giá trị với dòng mới, giá trị với ký tự trích dẫn bằng chữ, giá trị không in được, giá trị với ký tự toàn cầu, v.v.), sẽ nhận được một chút thú vị hơn.
Để ghi vào một tệp, đưa ra một loạt các đối số:
printf '%s\0' "${arguments[@]}" >file
... thay bằng "argument one"
, "argument two"
vv cho phù hợp.
Để đọc từ tệp đó và sử dụng nội dung của nó (trong bash, ksh93 hoặc shell khác gần đây với mảng):
declare -a args=()
while IFS='' read -r -d '' item; do
args+=( "$item" )
done <file
run_your_command "${args[@]}"
Để đọc từ tệp đó và sử dụng nội dung của nó (trong một vỏ không có mảng; lưu ý rằng điều này sẽ ghi đè lên danh sách đối số dòng lệnh cục bộ của bạn và do đó được thực hiện tốt nhất bên trong hàm, sao cho bạn ghi đè lên các đối số của hàm và không danh sách toàn cầu):
set --
while IFS='' read -r -d '' item; do
set -- "$@" "$item"
done <file
run_your_command "$@"
Lưu ý rằng -d
(cho phép sử dụng một dấu phân cách cuối dòng khác nhau) là một phần mở rộng không phải POSIX và một vỏ không có mảng cũng có thể không hỗ trợ nó. Nếu đó là trường hợp, bạn có thể cần sử dụng ngôn ngữ không vỏ để chuyển đổi nội dung được phân định bằng NUL thành eval
dạng an toàn:
quoted_list() {
## Works with either Python 2.x or 3.x
python -c '
import sys, pipes, shlex
quote = pipes.quote if hasattr(pipes, "quote") else shlex.quote
print(" ".join([quote(s) for s in sys.stdin.read().split("\0")][:-1]))
'
}
eval "set -- $(quoted_list <file)"
run_your_command "$@"
Đây là cách tôi chuyển nội dung của tệp dưới dạng đối số cho lệnh:
./foo --bar "$(cat ./bar.txt)"
$(<bar.txt)
(khi sử dụng shell, chẳng hạn như bash, với phần mở rộng thích hợp). $(<foo)
là một trường hợp đặc biệt: không giống như thay thế lệnh thông thường, như được sử dụng trong $(cat ...)
, nó không tách ra một lớp con để hoạt động, và do đó tránh được rất nhiều chi phí.
Nếu tất cả những gì bạn cần làm là chuyển tệp arguments.txt
có nội dung
arg1
arg2
argN
vào my_command arg1 arg2 argN
sau đó bạn có thể chỉ cần sử dụng xargs
:
xargs -a arguments.txt my_command
Bạn có thể đặt các đối số tĩnh bổ sung trong xargs
cuộc gọi, như thế xargs -a arguments.txt my_command staticArg
sẽ gọimy_command staticArg arg1 arg2 argN
Trong bash shell của tôi, những thứ sau đây hoạt động như một lá bùa:
cat input_file | xargs -I % sh -c 'command1 %; command2 %; command3 %;'
trong đó input_file là
arg1
arg2
arg3
Rõ ràng, điều này cho phép bạn thực thi nhiều lệnh với mỗi dòng từ input_file, một mẹo nhỏ hay mà tôi đã học ở đây .
$(somecommand)
, bạn sẽ thực hiện lệnh đó thay vì chuyển qua dưới dạng văn bản. Tương tự như vậy, >/etc/passwd
sẽ được xử lý dưới dạng chuyển hướng và ghi đè /etc/passwd
(nếu được chạy với quyền phù hợp), v.v.
xargs -d $'\n' sh -c 'for arg; do command1 "$arg"; command2 "arg"; command3 "arg"; done' _
- và cũng hiệu quả hơn, vì nó truyền càng nhiều đối số cho mỗi shell càng tốt thay vì bắt đầu một shell trên mỗi dòng trong tệp đầu vào của bạn.
cat
Sau khi chỉnh sửa câu trả lời của @Wesley Rice một vài lần, tôi quyết định những thay đổi của mình chỉ là quá lớn để tiếp tục thay đổi câu trả lời của anh ấy thay vì tự viết. Vì vậy, tôi quyết định tôi cần phải viết riêng của mình!
Đọc từng dòng của một tệp trong và hoạt động trên từng dòng như thế này:
#!/bin/bash
input="/path/to/txt/file"
while IFS= read -r line
do
echo "$line"
done < "$input"
Điều này đến trực tiếp từ tác giả Vivek Gite tại đây: https://www.cyberciti.biz/faq/unix-howto-read-line-by-line-from-file/ . Anh ta nhận được tín dụng!
Cú pháp: Đọc từng dòng tệp trên shell Bash Unix & Linux:
1. Cú pháp như sau đối với bash, ksh, zsh và tất cả các shell khác để đọc một dòng tệp theo dòng
2.while read -r line; do COMMAND; done < input.file
3.-r
Tùy chọn được truyền để đọc lệnh ngăn chặn dấu gạch chéo ngược thoát khỏi bị giải thích.
4. ThêmIFS=
tùy chọn trước khi đọc lệnh để ngăn khoảng trắng đầu / cuối bị cắt bớt -
5.while IFS= read -r line; do COMMAND_on $line; done < input.file
Và bây giờ để trả lời câu hỏi đã đóng này mà tôi cũng đã có: Có thể `git add` một danh sách các tệp từ một tệp không? - đây là câu trả lời của tôi:
Lưu ý rằng đó FILES_STAGED
là một biến chứa đường dẫn tuyệt đối đến một tệp chứa một loạt các dòng trong đó mỗi dòng là một đường dẫn tương đối đến một tệp mà tôi muốn thực hiện git add
. Đoạn mã này sắp trở thành một phần của tệp "eRCaGuy_dotfiles / hữu_scripts / sync_git numpo_to_build_machine.sh" trong dự án này, để cho phép đồng bộ hóa dễ dàng các tệp đang phát triển từ một PC (ví dụ: máy tính tôi mã hóa) Máy tính mạnh hơn tôi xây dựng trên): https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles .
while IFS= read -r line
do
echo " git add \"$line\""
git add "$line"
done < "$FILES_STAGED"
git add
trên đó: Có thể `git add` một danh sách các tệp từ một tệp không?