Powiedz, mam plik foo.txt
określający N
argumenty
arg1
arg2
...
argN
które muszę przekazać do polecenia my_command
Jak używać wierszy pliku jako argumentów polecenia?
Powiedz, mam plik foo.txt
określający N
argumenty
arg1
arg2
...
argN
które muszę przekazać do polecenia my_command
Jak używać wierszy pliku jako argumentów polecenia?
Odpowiedzi:
Jeśli twoja powłoka to bash (między innymi), skrót do $(cat afile)
to $(< afile)
, więc napiszesz:
mycommand "$(< file.txt)"
Udokumentowane na stronie podręcznika bash w sekcji „Zastępowanie poleceń”.
Alternatywnie, poproś o odczytanie polecenia ze stdin, więc: mycommand < file.txt
<
operatora. Oznacza to, że sama powłoka wykona przekierowanie, a nie wykona cat
plik binarny ze względu na swoje właściwości przekierowania.
"$(…)"
tego zawartość file.txt
byłaby przekazywana na standardowe wejście programu mycommand
, a nie jako argument. "$(…)"
oznacza uruchomienie polecenia i zwrócenie wyniku w postaci łańcucha ; tutaj „polecenie” czyta tylko plik, ale może być bardziej złożone.
Jak już wspomniano, możesz użyć lewych apostrofów lub $(cat filename)
.
To, o czym nie wspomniano, i myślę, że jest to ważne, to fakt, że musisz pamiętać, że powłoka rozbije zawartość tego pliku według białych znaków, podając każde znalezione słowo jako argument do polecenia. I chociaż możesz być w stanie ująć argument wiersza poleceń w cudzysłów, tak aby mógł zawierać spacje, sekwencje specjalne itp., Czytanie z pliku nie da tego samego. Na przykład, jeśli twój plik zawiera:
a "b c" d
argumenty, które otrzymasz, to:
a
"b
c"
d
Jeśli chcesz pobrać każdy wiersz jako argument, użyj konstrukcji while / read / do:
while read i ; do command_name $i ; done < filename
read -r
chyba że chcesz rozwinąć sekwencje ucieczki odwrotnym ukośnikiem - a NUL jest bezpieczniejszym separatorem niż nowa linia, szczególnie jeśli argumenty, które przekazujesz, to takie rzeczy jak nazwy plików, które mogą zawierać dosłowne znaki nowej linii. Ponadto bez czyszczenia IFS otrzymujesz początkowe i końcowe białe znaki niejawnie usuwane z i
.
command `< file`
przekaże zawartość pliku do polecenia na stdin, ale usunie znaki nowej linii, co oznacza, że nie możesz iterować po każdej linii osobno. W tym celu możesz napisać skrypt z pętlą 'for':
for line in `cat input_file`; do some_command "$line"; done
Lub (wariant wieloliniowy):
for line in `cat input_file`
do
some_command "$line"
done
Lub (wariant wieloliniowy z $()
zamiast ``
):
for line in $(cat input_file)
do
some_command "$line"
done
< file
odwrotnych znaków oznacza, że w rzeczywistości w ogóle nie wykonuje przekierowania command
.
command `< file`
Nie działa ani w bashu, ani w POSIX sh; zsh to inna sprawa, ale nie o to chodzi w tym pytaniu).
Hello World
w jednej linii. Zobaczysz go najpierw uruchomić some_command Hello
, a następnie some_command World
, nie some_command "Hello World"
bądź some_command Hello World
.
Robisz to za pomocą grawitacji:
echo World > file.txt
echo Hello `cat file.txt`
echo "Hello * Starry * World" > file.txt
w pierwszym etapie, można uzyskać co najmniej cztery oddzielne argumenty przekazywane do drugiego polecenia - i prawdopodobnie więcej, ponieważ *
s rozwiną się do nazw plików obecnych w bieżącym katalogu.
fork()
wychodzi z podpowłoki z FIFO dołączonym do jej /bin/cat
standardowego wyjścia , a następnie wywołuje jako element potomny tej podpowłoki, a następnie odczytuje wynik przez FIFO; porównaj z $(<file.txt)
, który wczytuje zawartość pliku bezpośrednio do basha, bez użycia podpowłok lub FIFO.
Jeśli chcesz to zrobić w niezawodny sposób, który działa dla każdego możliwego argumentu wiersza poleceń (wartości ze spacjami, wartości ze znakami nowej linii, wartości ze znakami cudzysłowu, wartości niedrukowalne, wartości ze znakami glob, itp.), Dostaje trochę bardziej interesujące.
Aby zapisać do pliku, mając tablicę argumentów:
printf '%s\0' "${arguments[@]}" >file
... zastąpić "argument one"
, odpowiednio "argument two"
, itp.
Aby czytać z tego pliku i używać jego zawartości (w bash, ksh93 lub innej niedawnej powłoce z tablicami):
declare -a args=()
while IFS='' read -r -d '' item; do
args+=( "$item" )
done <file
run_your_command "${args[@]}"
Aby czytać z tego pliku i używać jego zawartości (w powłoce bez tablic; zwróć uwagę, że spowoduje to nadpisanie lokalnej listy argumentów wiersza poleceń, a zatem najlepiej zrobić to wewnątrz funkcji, tak aby nadpisywać argumenty funkcji, a nie lista globalna):
set --
while IFS='' read -r -d '' item; do
set -- "$@" "$item"
done <file
run_your_command "$@"
Zauważ, że -d
(zezwalanie na użycie innego separatora końca linii) jest rozszerzeniem innym niż POSIX, a powłoka bez tablic również może go nie obsługiwać. W takim przypadku może być konieczne użycie języka innego niż powłoka, aby przekształcić zawartość rozdzielaną wartością NUL do postaci eval
bezpiecznej:
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 "$@"
Oto jak przekazuję zawartość pliku jako argument do polecenia:
./foo --bar "$(cat ./bar.txt)"
$(<bar.txt)
(przy użyciu powłoki, takiej jak bash, z odpowiednim rozszerzeniem). $(<foo)
jest przypadkiem specjalnym: w przeciwieństwie do zwykłego podstawiania poleceń, jak jest używane w $(cat ...)
, nie rozwidla podpowłoki do wykonywania operacji, a tym samym unika dużego narzutu.
Jeśli wszystko, co musisz zrobić, to włączyć plik arguments.txt
z zawartością
arg1
arg2
argN
na my_command arg1 arg2 argN
czym można po prostu użyć xargs
:
xargs -a arguments.txt my_command
W xargs
wywołaniu możesz umieścić dodatkowe argumenty statyczne , takie jak xargs -a arguments.txt my_command staticArg
which will callmy_command staticArg arg1 arg2 argN
W mojej powłoce bash działało jak urok:
cat input_file | xargs -I % sh -c 'command1 %; command2 %; command3 %;'
gdzie plik_wejściowy to
arg1
arg2
arg3
Jak widać, pozwala to na wykonanie wielu poleceń w każdym wierszu z pliku input_file, niezła sztuczka, której się tutaj nauczyłem .
$(somecommand)
, polecenie zostanie wykonane, a nie przekazane jako tekst. Podobnie >/etc/passwd
zostanie przetworzone jako przekierowanie i nadpisanie /etc/passwd
(jeśli uruchomione z odpowiednimi uprawnieniami) itp.
xargs -d $'\n' sh -c 'for arg; do command1 "$arg"; command2 "arg"; command3 "arg"; done' _
- a także bardziej wydajne, ponieważ przekazuje jak najwięcej argumentów do każdej powłoki, zamiast uruchamiać po jednej powłoce w każdym wierszu pliku wejściowego.
cat
Po kilkukrotnym edytowaniu odpowiedzi @Wesley Rice zdecydowałem, że moje zmiany stały się zbyt duże, aby kontynuować zmianę jego odpowiedzi zamiast pisać własną. Więc zdecydowałem, że muszę napisać własne!
Przeczytaj każdą linię pliku i wykonuj na niej linijka po linii w następujący sposób:
#!/bin/bash
input="/path/to/txt/file"
while IFS= read -r line
do
echo "$line"
done < "$input"
Pochodzi bezpośrednio od autora Vivek Gite tutaj: https://www.cyberciti.biz/faq/unix-howto-read-line-by-line-from-file/ . Dostaje kredyt!
Składnia: Czytaj plik wiersz po wierszu w powłoce Bash Unix i Linux:
1. Składnia jest następująca dla powłoki bash, ksh, zsh i wszystkich innych, aby odczytać plik wiersz po wierszu
2.while read -r line; do COMMAND; done < input.file
3.-r
Opcja przekazana do polecenia odczytu zapobiega interpretacji znaków ucieczki odwrotnego ukośnika.
4. DodajIFS=
opcję przed poleceniem odczytu, aby zapobiec przycinaniu wiodących / końcowych białych znaków -
5.while IFS= read -r line; do COMMAND_on $line; done < input.file
A teraz, aby odpowiedzieć na to teraz zamknięte pytanie, które też miałem: Czy jest możliwe dodanie listy plików z pliku za pomocą polecenia git? - oto moja odpowiedź:
Zauważ, że FILES_STAGED
jest to zmienna zawierająca bezwzględną ścieżkę do pliku, który zawiera kilka linii, gdzie każda linia jest ścieżką względną do pliku, na którym chciałbym to zrobić git add
. Ten fragment kodu wkrótce stanie się częścią pliku „eRCaGuy_dotfiles / possible_scripts / sync_git_repo_to_build_machine.sh” w tym projekcie, aby umożliwić łatwą synchronizację tworzonych plików z jednego komputera (np. Komputera, na którym koduję ) z innym (np. mocniejszy komputer, na którym buduję): https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles .
while IFS= read -r line
do
echo " git add \"$line\""
git add "$line"
done < "$FILES_STAGED"
git add
na nim: Czy jest możliwe dodanie listy plików z pliku za pomocą polecenia git?