GNU/Linux >> Znalost Linux >  >> Linux

Rozumíte „ifs=Read -r Line“?

Pochopitelně chápu, že lze přidat hodnotu do proměnné interního oddělovače polí. Například:

$ IFS=blah
$ echo "$IFS"
blah
$ 

Rozumím také tomu, že read -r line uloží data z stdin do proměnné s názvem line :

$ read -r line <<< blah
$ echo "$line"
blah
$ 

Jak však může příkaz přiřadit hodnotu proměnné? A ukládá nejprve data z stdin do proměnné line a poté zadejte hodnotu line na IFS ?

Přijatá odpověď:

V prostředí POSIX read , bez jakékoli možnosti nečte řádek , čte slova z řádku (pravděpodobně se zpětným lomítkem), kde slova jsou $IFS oddělovač a zpětné lomítko lze použít k opuštění oddělovačů (nebo pokračování v řádcích).

Obecná syntaxe je:

read word1 word2... remaining_words

read čte stdin jeden bajt po druhém¹, dokud nenajde neuvedený znak nového řádku (nebo konec vstupu), rozdělí jej podle složitých pravidel a uloží výsledek tohoto rozdělení do $word1 , $word2$remaining_words .

Například na vstupu jako:

  <tab> foo bar baz   blah   blah
whatever whatever

a s výchozí hodnotou $IFS , read a b c by přiřadilo:

  • $afoo
  • $bbar baz
  • $cblah blahwhatever whatever

Pokud je nyní předán pouze jeden argument, nestane se z něj read line . Stále read remaining_words . Zpracování zpětného lomítka se stále provádí, prázdné znaky² IFS jsou stále odstraněny ze začátku a konce.

-r volba odstraní zpracování zpětného lomítka. Takže stejný příkaz výše s -r místo toho přiřadí

  • $afoo
  • $bbar
  • $cbaz blah blah

Nyní, pro část rozdělení, je důležité si uvědomit, že pro $IFS existují dvě třídy znaků :prázdné znaky IFS² (včetně mezery a tabulátoru (a nového řádku, i když zde na tom nezáleží, pokud nepoužijete -d), které jsou shodou okolností také ve výchozí hodnotě $IFS ) a ostatní. Zacházení s těmito dvěma třídami postav je odlišné.

S IFS=: (: nejedná se o prázdný znak IFS), vstup jako :foo::bar:: by bylo rozděleno na "" , "foo" , "" , bar a "" (a další "" u některých implementací na tom nezáleží, kromě read -a ). Zatímco když nahradíme to : s mezerou se rozdělení provádí pouze na foo a bar . To znamená, že první a koncové jsou ignorovány a jejich sekvence jsou považovány za jeden. Pro kombinování bílých a jiných znaků v $IFS platí další pravidla . Některé implementace mohou přidat/odebrat speciální úpravu zdvojením znaků v IFS (IFS=:: nebo IFS=' ' ).

Zde tedy platí, že pokud nechceme, aby byly odstraněny úvodní a koncové znaky prázdného znaku, musíme tyto prázdné znaky IFS odstranit z IFS.

I se znaky IFS bez mezer, pokud vstupní řádek obsahuje jeden (a pouze jeden) z těchto znaků a je to poslední znak v řádku (např. IFS=: read -r word na vstupu jako foo: ) s POSIX shelly (nikoli zsh ani nějaký pdksh verze), je tento vstup považován za jeden foo slovo, protože v těchto shellech jsou znaky $IFS jsou považováni za terminátory , tedy word bude obsahovat foo , nikoli foo: .

Takže kanonický způsob, jak číst jeden řádek vstupu pomocí read vestavěný je:

IFS= read -r line

(všimněte si, že pro většinu read implementace, které fungují pouze pro textové řádky, protože znak NUL není podporován kromě zsh ).

Související:Linux – Sdílet soubory mezi hostitelem Linuxu a hostem Windows?

Pomocí var=value cmd syntaxe zajišťuje IFS je nastaven jinak pouze po dobu trvání daného cmd příkaz.

Poznámka k historii

read builtin byl představen Bourneovým shellem a měl již číst slova , ne čáry. Existuje několik důležitých rozdílů s moderními shelly POSIX.

Bourne shell se read nepodporoval -r možnost (která byla zavedena shellem Korn), takže neexistuje žádný způsob, jak zakázat zpracování zpětného lomítka, kromě předběžného zpracování vstupu pomocí něčeho jako sed 's/\/&&/g' tam.

Bourne shell neměl tuto představu o dvou třídách postav (které opět zavedl ksh). V Bourne shellu procházejí všechny znaky stejným způsobem jako IFS mezery v ksh, tedy IFS=: read a b c na vstupu jako foo::bar by přiřadil bar na $b , nikoli prázdný řetězec.

V Bourne shellu s:

var=value cmd

Pokud cmd je vestavěný (jako read is), var zůstane nastaven na value za cmd skončil. To je zvláště důležité u $IFS protože v Bourne shellu $IFS se používá k rozdělení všeho, nejen rozšíření. Také pokud odstraníte znak mezery z $IFS v Bourne shellu "[email protected]" již nefunguje.

V Bourne shellu způsobí přesměrování složeného příkazu jeho spuštění v podshellu (v nejstarších verzích dokonce věci jako read var < file nebo exec 3< file; read var <&3 nefungovalo), takže v Bourne shellu bylo vzácné použít read pro cokoli kromě uživatelského vstupu na terminálu (kde má tato manipulace s pokračováním řádku smysl)

Některé Unices (jako HP/UX, jeden je také v util-linux ) stále mají line příkaz pro čtení jednoho řádku vstupu (to býval standardní příkaz UNIX až do Single UNIX Specification verze 2).

To je v podstatě stejné jako head -n 1 kromě toho, že čte jeden bajt po druhém, aby se ujistil, že nečte více než jeden řádek. V těchto systémech můžete:

line=`line`

To samozřejmě znamená vytvořit nový proces, provést příkaz a přečíst jeho výstup pomocí kanálu, takže je mnohem méně efektivní než IFS= read -r line od ksh , ale stále mnohem intuitivnější.


Linux
  1. Zaneprázdněná schránka Číst soubor řádek po řádku?

  2. Číst řádkově orientovaný soubor, který nemusí končit novým řádkem?

  3. Nástroj příkazového řádku k načtení hesla, které nemá zpět echo?

  1. Rozumíte Linux Desktopu?

  2. Jak číst předposlední řádek v souboru pomocí Bash?

  3. Shell Script, čtěte na stejném řádku po ozvěně zprávy

  1. Pochopení YAML pro Ansible

  2. Rozumět Ifs?

  3. Příkaz pro výstup každého řádku dopředu a poté zpět