GNU/Linux >> LINUX-Kenntnisse >  >> Linux

„ifs=Read -r Line“ verstehen?

Ich verstehe natürlich, dass man der internen Feldtrennervariablen einen Wert hinzufügen kann. Zum Beispiel:

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

Ich verstehe auch, dass read -r line speichert Daten von stdin in die Variable namens line :

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

Wie kann ein Befehl jedoch einen Variablenwert zuweisen? Und speichert es zuerst Daten von stdin zur Variablen line und geben Sie dann den Wert von line an zu IFS ?

Akzeptierte Antwort:

In POSIX-Shells read , ohne Option liest keine Zeile , es liest Wörter aus einer (möglicherweise mit Backslash fortgesetzten) Zeile, in der Wörter $IFS sind delimited und Backslash können verwendet werden, um die Trennzeichen (oder Fortsetzungszeilen) zu maskieren.

Die allgemeine Syntax lautet:

read word1 word2... remaining_words

read liest stdin Byte für Byte¹, bis es ein Zeilenumbruchzeichen ohne Escapezeichen (oder das Ende der Eingabe) findet, teilt es nach komplexen Regeln auf und speichert das Ergebnis dieser Aufteilung in $word1 , $word2$remaining_words .

Zum Beispiel bei einer Eingabe wie:

  <tab> foo bar baz   blah   blah
whatever whatever

und mit dem Standardwert $IFS , read a b c würde zuweisen:

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

Wenn jetzt nur ein Argument übergeben wird, wird das nicht zu read line . Es ist immer noch read remaining_words . Die Backslash-Verarbeitung erfolgt weiterhin, IFS-Leerzeichen² werden weiterhin am Anfang und am Ende entfernt.

Das -r Option entfernt die Backslash-Verarbeitung. Also derselbe Befehl oben mit -r würde stattdessen zuweisen

  • $afoo
  • $bbar
  • $cbaz blah blah

Nun, für den Aufteilungsteil ist es wichtig zu erkennen, dass es zwei Klassen von Zeichen für $IFS gibt :die IFS-Leerzeichen² (einschließlich Leerzeichen und Tabulator (und Zeilenumbruch, obwohl das hier keine Rolle spielt, es sei denn, Sie verwenden -d), die zufällig auch im Standardwert von $IFS enthalten sind ) und die Anderen. Die Behandlung dieser beiden Zeichenklassen ist unterschiedlich.

Mit IFS=: (: kein IFS-Leerzeichen), eine Eingabe wie :foo::bar:: würde in "" aufgeteilt werden , "foo" , "" , bar und "" (und ein zusätzliches "" bei einigen Implementierungen spielt das aber keine Rolle, außer bei read -a ). Während wir diesen : ersetzen mit Leerzeichen erfolgt die Aufteilung nur in foo und bar . Das heißt, führende und nachfolgende werden ignoriert, und Sequenzen von ihnen werden wie eine behandelt. Es gibt zusätzliche Regeln, wenn Whitespace- und Nicht-Whitespace-Zeichen in $IFS kombiniert werden . Einige Implementierungen können die Sonderbehandlung hinzufügen/entfernen, indem sie die Zeichen in IFS verdoppeln (IFS=:: oder IFS=' ' ).

Wenn wir also hier nicht wollen, dass die führenden und nachgestellten Leerzeichen ohne Escapezeichen entfernt werden, müssen wir diese IFS-Leerzeichen aus IFS entfernen.

Selbst bei IFS-Nicht-Leerzeichen, wenn die Eingabezeile eines (und nur eines) dieser Zeichen enthält und es das letzte Zeichen in der Zeile ist (wie IFS=: read -r word bei einer Eingabe wie foo: ) mit POSIX-Shells (nicht zsh noch irgendein pdksh Versionen), wird diese Eingabe als ein foo betrachtet Wort, weil in diesen Shells die Zeichen $IFS gelten als Terminatoren , also word wird foo enthalten , nicht foo: .

Also die kanonische Art, eine Eingabezeile mit read zu lesen eingebaut ist:

IFS= read -r line

(Beachten Sie, dass für die meisten read Implementierungen, die nur für Textzeilen funktionieren, da das NUL-Zeichen außer in zsh nicht unterstützt wird ).

Verwandt:Linux – Dateien zwischen Linux-Host und Windows-Gast teilen?

Verwenden von var=value cmd Syntax stellt sicher, dass IFS wird nur für die Dauer dieses cmd anders gesetzt Befehl.

Anmerkung zur Geschichte

Der read builtin wurde von der Bourne-Shell eingeführt und sollte bereits Wörter lesen , keine Linien. Es gibt ein paar wichtige Unterschiede zu modernen POSIX-Shells.

read der Bourne-Shell unterstützte kein -r -Option (die von der Korn-Shell eingeführt wurde), sodass es keine andere Möglichkeit gibt, die Backslash-Verarbeitung zu deaktivieren, als die Eingabe mit so etwas wie sed 's/\/&&/g' vorzuverarbeiten dort.

Die Bourne-Shell hatte diese Vorstellung von zwei Klassen von Zeichen (die wiederum von ksh eingeführt wurden) nicht. In der Bourne-Shell werden alle Zeichen genauso behandelt wie IFS-Leerzeichen in ksh, also IFS=: read a b c bei einer Eingabe wie foo::bar würde bar zuweisen zu $b , nicht die leere Zeichenfolge.

In der Bourne-Shell mit:

var=value cmd

Wenn cmd ist eine eingebaute (wie read ist), var bleibt auf value gesetzt nach cmd hat beendet. Das ist besonders kritisch bei $IFS weil in der Bourne-Shell $IFS wird verwendet, um alles aufzuteilen, nicht nur die Erweiterungen. Auch, wenn Sie das Leerzeichen aus $IFS entfernen in der Bourne-Shell "[email protected]" funktioniert nicht mehr.

In der Bourne-Shell führt das Umleiten eines zusammengesetzten Befehls dazu, dass er in einer Subshell ausgeführt wird (in den frühesten Versionen wurden sogar Dinge wie read var < file oder exec 3< file; read var <&3 lesen hat nicht funktioniert), daher wurde in der Bourne-Shell selten read verwendet für alles außer Benutzereingaben auf dem Terminal (wo diese Zeilenfortsetzungsbehandlung sinnvoll war)

Einige Unices (wie HP/UX, es gibt auch einen in util-linux ) haben noch eine line Befehl zum Lesen einer Eingabezeile (bis zur Version 2 der Single UNIX Specification war dies ein Standard-UNIX-Befehl).

Das ist im Grunde dasselbe wie head -n 1 außer dass es jeweils ein Byte liest, um sicherzustellen, dass es nicht mehr als eine Zeile liest. Auf diesen Systemen können Sie Folgendes tun:

line=`line`

Das bedeutet natürlich, einen neuen Prozess zu erzeugen, einen Befehl auszuführen und seine Ausgabe über eine Pipe zu lesen, also viel weniger effizient als die IFS= read -r line von ksh , aber immer noch viel intuitiver.


Linux
  1. Eine zeilenorientierte Datei lesen, die nicht mit einem Zeilenumbruch enden darf?

  2. Befehlszeilenprogramm zum Abrufen von Passwörtern ohne Echo?

  3. Linux-Desktop verstehen?

  4. Wie liest man die vorletzte Zeile in einer Datei mit Bash?

  5. Shell-Skript, nach dem Echo einer Nachricht in derselben Zeile lesen

Grundlegendes zu Linux-Dateiberechtigungen

Bash las Befehl

So lesen Sie eine Datei Zeile für Zeile in Bash

Diff-Befehl unter Linux

Prozesse unter Linux verstehen

Wie liest man Befehlszeilenargumente in Shell-Skripten?