de-vraag
  • Klausimai
  • Žymos
  • Vartotojai
Pranešimai
Apdovanojimai
Registracija
Užsiregistravę gausite pranešimus apie atsakymus ir komentarus į savo klausimus.
Prisijungti
Jei jau turite paskyrą, prisijunkite ir patikrinkite naujus pranešimus.
Už pridėtus klausimus, atsakymus ir komentarus bus skiriami apdovanojimai.
Daugiau
Šaltinis
Redaguoti
 Thelema
Thelema
Question

Kaip "Bash" scenarijuje iteruoti argumentus

Turiu sudėtingą komandą, iš kurios norėčiau sukurti shell/bash scenarijų. Ją galiu lengvai užrašyti kaip $1:

foo $1 args -o $1.ext

Noriu, kad scenarijui būtų galima perduoti kelis įvesties vardus. Kaip teisingai tai padaryti?

Ir, žinoma, noriu tvarkyti failų pavadinimus su tarpais.

830 2008-11-01T18:14:21+00:00 3
 codeforester
codeforester
Redaguotas klausimas priešpietr sausis 2017 в 2:16
Programavimas
command-line
bash
Šis klausimas turi 1 atsakymas atsakymų anglų kalba, norėdami juos perskaityti prisijunkite prie savo paskyros.
Solution / Answer
Robert Gamble
Robert Gamble
popietr lapkritis 2008 в 6:25
2008-11-01T18:25:27+00:00
Daugiau
Šaltinis
Redaguoti
#8606336

Naudokite "[email protected]" visiems argumentams reikšti:

for var in "[email protected]"
do
    echo "$var"
done

Taip bus iteruojamas kiekvienas argumentas ir spausdinamas atskiroje eilutėje. [email protected] elgiasi kaip $*, išskyrus tai, kad cituojant argumentai tinkamai suskaidomi, jei juose yra tarpų:

sh test.sh 1 2 '3 4'
1
2
3 4
Robert Gamble
Robert Gamble
Redaguotas atsakymas popietr lapkritis 2008 в 11:52
1358
0
Jonathan Leffler
Jonathan Leffler
popietr lapkritis 2008 в 11:04
2008-11-01T23:04:33+00:00
Daugiau
Šaltinis
Redaguoti
#8606337

Perrašytas dabar jau ištrintas atsakymas pagal VonC. Robert Gamble's glaustame atsakyme tiesiogiai nagrinėjamas klausimas. Šiame atsakyme plačiau aptariami kai kurie klausimai, susiję su failų pavadinimais, kuriuose yra tarpų. Taip pat žr: ${1:+"[email protected]"} in /bin/sh Pagrindinė tezė: "[email protected]" yra teisinga, o $* (be kabučių) beveik visada klaidinga. Taip yra todėl, kad "[email protected]" veikia gerai, kai argumentuose yra tarpai, o veikia taip pat, kaip $*, kai jų nėra. Tam tikromis aplinkybėmis "$*" taip pat tinka, bet "[email protected]" paprastai (bet ne visada) veikia tose pačiose vietose. Neįrašyti [email protected] ir $* yra lygiaverčiai (ir beveik visada neteisingi). Taigi, kuo skiriasi $*, [email protected], "$*" ir "[email protected]"? Visi jie susiję su 'visais apvalkalo argumentais', tačiau atlieka skirtingus veiksmus. Kai $* ir [email protected] nėra cituojami, jie atlieka tą patį. Jie kiekvieną 'žodį' (ne baltųjų ženklų seką) traktuoja kaip atskirą argumentą. Tačiau cituojamos formos yra visiškai skirtingos: "$*" traktuoja argumentų sąrašą kaip vieną tarpeliais atskirtą eilutę, o "[email protected]" traktuoja argumentus beveik taip pat, kaip jie buvo nurodyti komandinėje eilutėje. "[email protected]" išsiplečia į nieką, kai nėra pozicionuojamųjų argumentų; "$*" išsiplečia į tuščią eilutę — ir taip, yra skirtumas, nors jį gali būti sunku pastebėti. Daugiau informacijos rasite toliau, po (nestandartinės) komandos al įvedimo. Antroji tezė: jei reikia apdoroti argumentus su tarpais ir tada juos perduoti kitoms komandoms, tuomet kartais reikia nestandartinių pagalbos priemonių. (Arba atsargiai naudokite masyvus: "${Masyvas[@]}" elgiasi analogiškai kaip "[email protected]".) Pavyzdys:

    $ mkdir "my dir" anotherdir
    $ ls
    anotherdir      my dir
    $ cp /dev/null "my dir/my file"
    $ cp /dev/null "anotherdir/myfile"
    $ ls -Fltr
    total 0
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 my dir/
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 anotherdir/
    $ ls -Fltr *
    my dir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 my file

    anotherdir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 myfile
    $ ls -Fltr "./my dir" "./anotherdir"
    ./my dir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 my file

    ./anotherdir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 myfile
    $ var='"./my dir" "./anotherdir"' && echo $var
    "./my dir" "./anotherdir"
    $ ls -Fltr $var
    ls: "./anotherdir": No such file or directory
    ls: "./my: No such file or directory
    ls: dir": No such file or directory
    $

Kodėl tai neveikia? Neveikia, nes apvalkalas apdoroja kabutes prieš jas išplėsdamas kintamuosius. Taigi, kad apvalkalas atkreiptų dėmesį į kabutes, įterptas į $var, turite naudoti eval:

    $ eval ls -Fltr $var
    ./my dir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 my file

    ./anotherdir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 myfile
    $ 

Tai tampa labai sudėtinga, kai turite tokius failų pavadinimus kaip "Jis sakė, "Nedarykite to!"" (su kabutėmis, dvigubomis kabutėmis ir tarpais).

    $ cp /dev/null "He said, \"Don't do this!\""
    $ ls
    He said, "Don't do this!"       anotherdir                      my dir
    $ ls -l
    total 0
    -rw-r--r--   1 jleffler  staff    0 Nov  1 15:54 He said, "Don't do this!"
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 anotherdir
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 my dir
    $ 

Kriauklės (visos) ne itin palengvina tokių todėl (kaip bebūtų keista) daugelis "Unix" programų nelabai gerai atlieka šį darbą. juos apdoroti. "Unix" sistemoje failo pavadinime (viename komponente) gali būti bet kokie simboliai, išskyrus pasvirąjį brūkšnį ir NUL '\0'. Tačiau apvalkalai primygtinai rekomenduoja nenaudoti tarpų, naujų eilučių ar skirtukų niekur kelio pavadinime. Dėl to ir standartiniuose Unix failų varduose nėra tarpų ir pan. Kai susiduriama su failų vardais, kuriuose gali būti tarpų ir kt. simbolių, turite būti labai atsargūs, ir aš pastebėjau, kad seniai, kad man prireikė programos, kuri nėra standartinė "Unix" sistemoje. Pavadinau ją escape (1.1 versijos data - 1989-08-23T16:01:45Z). Štai escape naudojimo pavyzdys - su SCCS valdymo sistema. Tai yra viršelio scenarijus, kuris atlieka ir delta (pagalvokite check-in), ir Get (pagalvokite check-out). Įvairūs argumentai, ypač -y (priežastis, dėl kurios atlikote pakeitimą) yra tuščių ir naujų eilučių. Atkreipkite dėmesį, kad scenarijus sukurtas 1992 m., todėl jame naudojamos atgalinės žymės, o ne $(cmd ...) ir pirmoje eilutėje nenaudoja #!/bin/sh.

:   "@(#)$Id: delget.sh,v 1.8 1992/12/29 10:46:21 jl Exp $"
#
#   Delta and get files
#   Uses escape to allow for all weird combinations of quotes in arguments

case `basename $0 .sh` in
deledit)    eflag="-e";;
esac

sflag="-s"
for arg in "[email protected]"
do
    case "$arg" in
    -r*)    gargs="$gargs `escape \"$arg\"`"
            dargs="$dargs `escape \"$arg\"`"
            ;;
    -e)     gargs="$gargs `escape \"$arg\"`"
            sflag=""
            eflag=""
            ;;
    -*)     dargs="$dargs `escape \"$arg\"`"
            ;;
    *)      gargs="$gargs `escape \"$arg\"`"
            dargs="$dargs `escape \"$arg\"`"
            ;;
    esac
done

eval delta "$dargs" && eval get $eflag $sflag "$gargs"

(Tikriausiai šiais laikais taip kruopščiai nenaudočiau escape - tai yra pavyzdžiui, nereikia su -e argumentu, bet apskritai tai yra vienas iš mano paprastesnių skriptų, naudojančių escape.) Programa escape tiesiog išveda savo argumentus, panašiai kaip echo tačiau ji užtikrina, kad argumentai būtų apsaugoti, kad juos būtų galima naudoti su eval (vienas iš eval lygių; turiu programą, kuri atliko nuotolinį shell vykdymą, ir jai reikėjo apsaugoti escape išvestį).

    $ escape $var
    '"./my' 'dir"' '"./anotherdir"'
    $ escape "$var"
    '"./my dir" "./anotherdir"'
    $ escape x y z
    x y z
    $ 

Turiu kitą programą, pavadintą al, kurios argumentai išvardijami po vieną eilutėje (ir ji dar senesnė: 1987-01-27T14:35:49 versija 1.1). Ji naudingiausia derinant scenarijus, nes ją galima prijungti prie į komandų eilutę, kad pamatytumėte, kokie argumentai iš tikrųjų perduodami komandai.

    $ echo "$var"
    "./my dir" "./anotherdir"
    $ al $var
    "./my
    dir"
    "./anotherdir"
    $ al "$var"
    "./my dir" "./anotherdir"
    $

[Pridėta: O dabar, norėdami parodyti skirtumą tarp įvairių "[email protected]" užrašų, pateikiame dar vieną pavyzdį:

$ cat xx.sh
set -x
al [email protected]
al $*
al "$*"
al "[email protected]"
$ sh xx.sh     *      */*
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al 'He said, "Don'\''t do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file'
He said, "Don't do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file
+ al 'He said, "Don'\''t do this!"' anotherdir 'my dir' xx.sh anotherdir/myfile 'my dir/my file'
He said, "Don't do this!"
anotherdir
my dir
xx.sh
anotherdir/myfile
my dir/my file
$

Atkreipkite dėmesį, kad niekas neišsaugo originalių tuščių vietų tarp * ir */* komandinėje eilutėje. Taip pat atkreipkite dėmesį, kad galite pakeisti 'komandinės eilutės argumentus' apvalkale naudodami:

set -- -new -opt and "arg with space"

Taip nustatomos 4 parinktys: 'naujas', 'opt', 'ir' ir 'arg su tarpais'.
] Hmm, tai'gana ilgas atsakymas - gal geriau tinka terminas egzegezė. Pirminį escape kodą galima gauti paprašius (el. paštu firstname dot lastname at gmail dot com). al šaltinio kodas yra neįtikėtinai paprastas:

#include <stdio.h>
int main(int argc, char **argv)
{
    while (*++argv != 0)
        puts(*argv);
    return(0);
}

Štai ir viskas. Tai yra lygiavertis test.sh scenarijus, kurį parodė Robertas Gamble'as, ir galėtų būti parašytas kaip apvalkalo funkcija (tačiau apvalkalo funkcijos neegzistavo vietinėje Bourne'o apvalkalo versijoje, kai pirmą kartą parašiau al). Taip pat atkreipkite dėmesį, kad al galite parašyti kaip paprastą apvalkalo scenarijų:

[ $# != 0 ] && printf "%s\n" "[email protected]"

Sąlyga reikalinga tam, kad jis nesukurtų išvesties, kai jam neperduodami jokie argumentai. Komanda printf išves tuščią eilutę tik su formato eilutės argumentu, tačiau C programa nieko neišves.

 Community
Community
Redaguotas atsakymas popietr gegužė 2017 в 12:18
227
0
Alok Singhal
Alok Singhal
popietr gruodis 2009 в 10:25
2009-12-31T22:25:49+00:00
Daugiau
Šaltinis
Redaguoti
#8606338

Atkreipkite dėmesį, kad Roberto atsakymas yra teisingas, jis veikia ir sh. Galite jį dar labiau supaprastinti:

for i in "[email protected]"

yra lygiavertis:

for i

T.y., jums nieko nereikia!

Testavimas ($ yra komandų eilutė):

$ set a b "spaces here" d
$ for i; do echo "$i"; done
a
b
spaces here
d
$ for i in "[email protected]"; do echo "$i"; done
a
b
spaces here
d

Pirmą kartą apie tai perskaičiau Kernighan ir Pike knygoje Unix programavimo aplinka.

bash, help for tai dokumentuoja:

... ;] do COMMANDS; done`

Jei 'in WORDS ...;' nėra, tuomet manoma, kad 'in "[email protected]"'.

 tripleee
tripleee
Redaguotas atsakymas priešpietr kovas 2017 в 9:28
125
0
Pridėti klausimą
Kategorijos
Visi
Technologijos
Kultūra / poilsis
Gyvenimas / Menai
Mokslas
Profesionalus
Verslas
Vartotojai
Visi
Naujas
Populiarus
1
Виталий Теслюк
Registruota prieš 7 valandas
2
shokir qochqorov
Registruota prieš 9 valandas
3
Roxana Elizabeth CASTILLO Avalos
Registruota prieš 1 savaitę
4
Hideo Nakagawa
Registruota prieš 1 savaitę
5
Sergiy Tytarenko
Registruota prieš 1 savaitę
DE
EL
ES
FR
ID
IT
JA
KO
LT
NL
PT
RU
TR
ZH
© de-vraag 2022
Šaltinis
stackoverflow.com
pagal licenciją cc by-sa 3.0 nurodant autorystę