Jag vill skriva ett skript som tar ett .txt
filnamn som argument, läser filraden för rad och skickar varje rad till ett kommando. Den kör till exempel command --option "LINE 1"
, sedan command --option "LINE 2"
, etc. Utdata från kommandot skrivs till en annan fil. Hur ska jag göra det? Jag vet inte var jag ska börja.
Svar
Använd while read
loop:
: > another_file ## Truncate file. while IFS= read -r LINE; do command --option "$LINE" >> another_file done < file
Ett annat är att omdirigera utdata med block:
while IFS= read -r LINE; do command --option "$LINE" done < file > another_file
Det sista är att öppna filen:
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
Om ett av kommandona läser inmatning skulle det vara bra idé att använda en annan fd för inmatning så kommandona kommer inte att äta den (här förutsatt att ksh
, zsh
eller bash
för -u 3
, använd <&3
istället bärbart):
while IFS= read -ru 3 LINE; do ... done 3< file
Fin allierad för att acceptera argument kan du göra:
#!/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"
Vilken man kan köra som:
bash script.sh file another_file
Extra idé. Använd bash
, använd readarray
:
readarray -t LINES < "$FILE" for LINE in "${LINES[@]}"; do ... done
Obs: IFS=
kan utelämnas om du inte tänker ha linjevärden trimmade av ledande och efterföljande mellanslag.
Svar
Ett annat alternativ är xargs
.
Med GNU xargs
:
xargs -a file -I{} -d"\n" command --option {} other args
{}
är platshållaren för textraden.
Annat xargs
har vanligtvis inte -a
, -d
, men vissa har -0
för NUL-avgränsad ingång. Med dessa kan du göra:
< file tr "\n" "\0" | xargs -0 -I{} command --option {} other args
På Unix-överensstämmande system (-I
är valfritt i POSIX och endast krävs för UNIX-överensstämmande system), måste du förbehandla ingången för att citera raderna i det format som förväntas av xargs
:
< file sed "s/"/"\\""/g;s/.*/"&"/" | xargs -E "" -I{} command --option {} other args
Observera dock att vissa xargs
-implementeringar har en mycket låg gräns för argumentets maximala storlek (till exempel 255 på Solaris, det lägsta tillåtna enligt Unix-specifikationen).
Kommentarer
Svar
Att hålla exakt till frågan:
#!/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
Kommentarer
- fungerade som charm 🙂
- +1 älskade det här, tack
Svar
Det bästa svaret jag har und is:
for i in `cat`; do "$cmd" "$i"; done < $file
EDIT:
… fyra år senare …
efter flera nedröstningar och lite mer erfarenhet skulle jag rekommendera följande nu:
xargs -l COMMAND < file
Kommentarer
- Detta anropar kommandot en gång för varje ord i filen. Du bör också citera referenser till skalvariabler (som i
do "$cmd" "$i";
) såvida du inte har en anledning att inte göra det; om filen innehöll en*
som ett ord i sig, skulle din kod köras$cmd *
, vilket naturligtvis skulle köra kommandot med en lista över filerna i den aktuella katalogen. - @ G-Man, utom i
zsh
,`cat`
skulle redan expandera*
(det ociterade$i
kan fortfarande utvidga ett jokertecken (en andra omgång) om expansionen av`cat`
introducerar några). Hur som helst är den metoden verkligen fel. - +1. Detta kommer att fungera bra, om varje rad bara innehåller den ingång som behövs för att kommandot ska användas utan mellanslag.
- +1, gillade xargs-lösning
- Detta fungerar! (den senare redigeringen
xargs -l COMMAND < file
). Tack!
Svar
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
UTGÅNG
--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
Svar
ed file.txt %g/^/s// / 2,$g/^/-,.j 1s/^/command/ wq chmod 755 file.txt ./file.txt
Ta alla rader i en fil och skicka dem som argument till ett enda kommando, dvs
command line1 line2 line3 ....
Om du behöver --option
flagga före varje rad ändra det andra kommandot till:
%g/^/s// --option /
Kommentarer
- Vad -1 för använder ed … verkligen?
- Jag ’ inte nedröstade dig, men personen som förmodligen hade följande skäl: (1) Detta gör inte vad frågan ställer efter. Detta åberopar kommandot en gång med allt innehåll i filen på kommandoraden; snarare än en gång per rad .(2) Detta gör ingenting för att hantera tecken som är speciella för skalet (som kan finnas i filen); t.ex.
'
,"
,<
,>
,;
, etc. (3) Detta skapar en onödig tillfällig fil. (4) Saker som detta görs vanligtvis med ”här dokument”. (5) Dinaed
kommandon är klumpiga; de två första kommandona kan reduceras till%s/^/ /
och%j
.
<file xargs -L 1 -I{} command --option {} other args