Hallo zusammen, ich habe folgende Struktur am Laufen: #!/bin/bash zaehler=0 egrep -a -A1 ´(...egal...)´ "$myfile" | while read line do ... if [ ... ] then zaehler=`expr $zaehler + 1` ... fi if then ... fi ... ... done exit Solange ich $zaehler innerhalb der while-Schleife abfrage, bekomme ich die richtigen Werte. Frage ich $zaehler nach done ab, dann erhalte ich 0. Sicher habe ich heute zu lange an der Kiste gesessen um die simple Loesung zu finden:-( Ich werde wohl erst einmal Schlafen gehen:-) Vielleicht sieht ja trotzdem inzwischen jemand den Fehler:-) MfG Th. Moritz -- Geisterfahrer sind oft sehr entgegenkommend!
On Mon, 11 Aug 2003 at 22:20 (+0200), Thomas Moritz wrote:
Hallo zusammen, ich habe folgende Struktur am Laufen:
#!/bin/bash
zaehler=0
egrep -a -A1 ´(...egal...)´ "$myfile" | while read line do ... if [ ... ] then zaehler=`expr $zaehler + 1` ... fi if then ... fi ... ... done
exit
Solange ich $zaehler innerhalb der while-Schleife abfrage, bekomme ich die richtigen Werte. Frage ich $zaehler nach done ab, dann erhalte ich 0. Sicher habe ich heute zu lange an der Kiste gesessen um die simple Loesung zu finden:-( Ich werde wohl erst einmal Schlafen gehen:-) Vielleicht sieht ja trotzdem inzwischen jemand den Fehler:-)
Das ist kein Fehler. Aus *man bash*, Abschnitt *Pipelines*: Each command in a pipeline is executed as a separate pro cess (i.e., in a subshell). Zuerst: Mich irritiert die Option -a im egrep - was willst Du damit erreichen? Bei einem Binary sind Zeilen ja nicht so aussagekräftig ;-) Die while-Schleife läuft also in einer Subshell, und die dort verwendeten Variablen findest Du in der aufrufenden Shell (dem Script) nicht wieder. Ich sehe 3 Varianten (alles ungetestet): 1. temp. Datei: <schnipp> export zaehler=0 egrep -a -A1 '(...egal...)' "$myfile" | while read line do ... if [ ... ] then zaehler=`expr $zaehler + 1` # Zaehler in temp. Datei, immer wieder ueberschreiben echo $zaehler >temp_datei ... fi if then ... fi ... ... done # letzten Wert aus temp. Datei holen zaehler=`cat temp_datei` exit <schnapp> 2. Kommandosubstition: <schnipp> export zaehler=0 zaehler=`egrep -a -A1 '(...egal...)' "$myfile" | while read line do ... if [ ... ] then zaehler=\`expr $zaehler + 1\` # Zaehler ausgeben echo $zaehler ... fi if then ... fi ... ... # letzten ausgegebenen Wert behalten done | tail -1` exit <schnapp> Die 2. Variante kommt ohne temp. Dateien aus, hat aber den Nachteil, dass innerhalb der Schleife ein echo auf stdout passiert - wenn Du noch was anderes ausgeben willst, geht das nicht so (man könnte auf stderr ausweichen mit den restlichen Ausgaben). 3. Unter Umständen kannst Du auf die Pipe verzichten: <schnipp> zaehler=0 # alten Feldtrenner merken OFS=$IFS # neuer Feldtrenner: nur Newline IFS=' ' for line in `egrep -a -A1 '(...egal...)' "$myfile"`; do ... if [ ... ] then zaehler=`expr $zaehler + 1` ... fi if then ... fi ... ... done # Feldtrenner zuruecksetzen IFS=$OFS exit <schnapp> Damit legst Du den Input Field Separator auf Newline (Defaultmäßig gehören auch White Spaces dazu) und kannst dann die Zeilen per for-Schleife einlesen. Leerzeilen werden dann allerdings nicht gezählt, aber Dein egrep sucht ja wohl nach was Anderem ;-) Da fällt mir ein: Wenn Du Leerzeilen mitzählen willst, dann kannst Du das mit einem Trick machen: for line in `egrep -a -A1 '(...egal...)' "$myfile" | sed 's/^$/ /'`; do ... Damit ersetzt Du Leerzeilen durch eine Zeile mit einem Blank. Welche Variante bei Dir am besten passt, musst Du anhand Deines Scripts entscheiden (3. könnte z. B. schieflaufen, wenn Du innerhalb der Schleife nochmal den Original-IFS brauchst). Jan
Am Dienstag, 12. August 2003 01:09 schrieb Jan Trippler: Hallo Jan, besten Dank fuer Deine schnelle und umfangreiche Antwort!
On Mon, 11 Aug 2003 at 22:20 (+0200), Thomas Moritz wrote:
Hallo zusammen, ich habe folgende Struktur am Laufen:
#!/bin/bash
zaehler=0
egrep -a -A1 ´(...egal...)´ "$myfile" | while read line do ... if [ ... ] then zaehler=`expr $zaehler + 1` ... fi if then ... fi ... ... done
exit
Solange ich $zaehler innerhalb der while-Schleife abfrage, bekomme ich die richtigen Werte. Frage ich $zaehler nach done ab, dann erhalte ich 0. Sicher habe ich heute zu lange an der Kiste gesessen um die simple Loesung zu finden:-( Ich werde wohl erst einmal Schlafen gehen:-) Vielleicht sieht ja trotzdem inzwischen jemand den Fehler:-)
Das ist kein Fehler. Aus *man bash*, Abschnitt *Pipelines*: Each command in a pipeline is executed as a separate pro cess (i.e., in a subshell).
Ja, jetzt wo Du mich mit der Nase drauf stoesst:-)
Zuerst: Mich irritiert die Option -a im egrep - was willst Du damit erreichen? Bei einem Binary sind Zeilen ja nicht so aussagekräftig ;-)
Das File entsteht jeweils aus einem dd von einer RamCard. Die darin enthaltenen Daten sind teils ASCII und teils binaer! Anhand der enthaltenen Steuersequenzen fuer firmeninterne Hardware kann ich verschiedene Auswertungen vornehmen. Ohne -a komme ich da nicht weit. Wenn ich mich recht entsinne ist der egrep ohne -a sogar ausgestiegen. Ist schon ca. 2 Jahre her, seit ich die erste Version des Scriptes zuverlaessig am Laufen habe.
Die while-Schleife läuft also in einer Subshell, und die dort verwendeten Variablen findest Du in der aufrufenden Shell (dem Script) nicht wieder. Ich sehe 3 Varianten (alles ungetestet):
1. temp. Datei: <schnipp> export zaehler=0 egrep -a -A1 '(...egal...)' "$myfile" | while read line do ... if [ ... ] then zaehler=`expr $zaehler + 1` # Zaehler in temp. Datei, immer wieder ueberschreiben echo $zaehler >temp_datei ... # letzten Wert aus temp. Datei holen zaehler=`cat temp_datei`
Das ist die einfachste und sicherste Loesung, obwohl dabei wieder Zeit drauf geht. Aber darauf kommt es hierbei nicht so an. Die anderen Varianten sind von der Idee her auch gut, bringen mich jedoch in diesem speziellen Fall nicht weiter. Besten Dank nochmals! MfG Th. Moritz -- Geisterfahrer sind oft sehr entgegenkommend!
Hallo Jan, hallo Thomas, hallo Leute, Am Dienstag, 12. August 2003 01:09 schrieb Jan Trippler:
On Mon, 11 Aug 2003 at 22:20 (+0200), Thomas Moritz wrote:
Hallo zusammen, ich habe folgende Struktur am Laufen:
#!/bin/bash
zaehler=0
egrep -a -A1 ´(...egal...)´ "$myfile" | while read line do # [...] zaehler=`expr $zaehler + 1` # [...] done
exit
Solange ich $zaehler innerhalb der while-Schleife abfrage, bekomme ich die richtigen Werte. Frage ich $zaehler nach done ab, dann erhalte ich 0.
Das ist kein Fehler. Aus *man bash*, Abschnitt *Pipelines*: Each command in a pipeline is executed as a separate process (i.e., in a subshell). [...] Die while-Schleife läuft also in einer Subshell, und die dort verwendeten Variablen findest Du in der aufrufenden Shell (dem Script) nicht wieder.
ACK
Ich sehe 3 Varianten (alles ungetestet):
1. temp. Datei: [...] 2. Kommandosubstition: [...] 3. Unter Umständen kannst Du auf die Pipe verzichten: [...]
4. geschickte Ausweitung der Subshell ;-) Stark verkürztes Beispiel: #!/bin/bash zaehler=0 echo -e "a\nb\nc\nd" | { # ^----- hier startet die Subshell while read line do zaehler=`expr $zaehler + 1` done echo Anzahl: $zaehler exit } # erst hier endet die Subshell! Erklärung aus man bash: { list; } list is simply executed in the current shell envi- ronment. list must be terminated with a newline or semicolon. This is known as a group command. The return status is the exit status of list. Note that unlike the metacharacters ( and , { and } are reserved words and must occur where a reserved word is permitted to be recognized. Since they do not cause a word break, they must be separated from list by whitespace. BTW: Du verwendest Backticks, um die Variable "$zaehler"eins hochzuzählen: zaehler=`expr $zaehler + 1` Das startet ein externes Programm in einer Subshell - wenig performant ;-) Genausogut funktioniert die (vermutlich bash-spezifische) Variante zaehler=$(($zaehler+1)) - und das ohne Subshell ;-) Eine weitere Variante ohne Subshell wäre let zaehler=$zaehler+1 Und ich dachte schon, TMTOWTDI gilt nur für Perl ;-))) Gruß Christian Boltz -- Das Problem ist vermutlich auf schlechte Erdung zurueckzufuehren. Schlagt einfach zwei Stahlnaegel in die Turnschuhe eures MCSEs. So koennen die Ueberspannungen aus dem Kopf abfliessen, und Euer Mann achtet auch mehr auf seine Sicherheit in der Naehe von Computern. [M. Liss in dasr]
On Don, 14 Aug 2003 at 00:22 (+0200), Christian Boltz wrote: [...]
4. geschickte Ausweitung der Subshell ;-)
Stark verkürztes Beispiel:
#!/bin/bash zaehler=0
echo -e "a\nb\nc\nd" | { # ^----- hier startet die Subshell while read line do zaehler=`expr $zaehler + 1` done
echo Anzahl: $zaehler
exit } # erst hier endet die Subshell!
Jau, die ist auch schick - die fiel mir auf die Schnelle nicht ein.
BTW: Du verwendest Backticks, um die Variable "$zaehler"eins hochzuzählen: zaehler=`expr $zaehler + 1` Das startet ein externes Programm in einer Subshell - wenig performant ;-)
aber portabel.
Genausogut funktioniert die (vermutlich bash-spezifische) Variante zaehler=$(($zaehler+1)) - und das ohne Subshell ;-) Eine weitere Variante ohne Subshell wäre let zaehler=$zaehler+1
AFAIK alles bash-spezifisch. Sowas nehme ich nur dann, wenn es nicht anders geht ;)
Und ich dachte schon, TMTOWTDI gilt nur für Perl ;-)))
Das gilt immer (ausser vielleicht in VB ;) Jan
Hi, On Thu, 14 Aug 2003, Jan Trippler wrote:
4. geschickte Ausweitung der Subshell ;-)
echo -e "a\nb\nc\nd" | { # ^----- hier startet die Subshell
Keine subshell, sondern ein command group. Insbesondere wird diese group im Environment der aktuellen Shell ausgewertet. D.h. nach % ( bla=1 ) ist $bla nicht gesetzt (da nur in subshell veraendert, die schon wieder zu Ende ist, nach % { bla=1; } ist $bla gesetzt.
BTW: Du verwendest Backticks, um die Variable "$zaehler"eins hochzuzählen: zaehler=`expr $zaehler + 1` Das startet ein externes Programm in einer Subshell - wenig performant ;-)
aber portabel.
Siehe unten.
zaehler=$(($zaehler+1)) - und das ohne Subshell ;-) Eine weitere Variante ohne Subshell wäre let zaehler=$zaehler+1
AFAIK alles bash-spezifisch.
Falsch. Arithmetic expansion ist POSIX. Nur Solaris /bin/sh und ash (welche beide nicht POSIX konform sind) haben das nicht. Ciao, Micha.
Hallo Michael, hallo Jan, hallo Leute, Am Donnerstag, 14. August 2003 10:30 schrieb Michael Matz:
On Thu, 14 Aug 2003, Jan Trippler wrote:
4. geschickte Ausweitung der Subshell ;-)
echo -e "a\nb\nc\nd" | { # ^----- hier startet die Subshell
Keine subshell, sondern ein command group. Insbesondere wird diese group im Environment der aktuellen Shell ausgewertet. [...]
Jein ;-) Richtig ist: Die geschweiften Klammern { } starten keine Subshell. Aber: das Ganze sitzt ja hinter einer Pipe, und dadurch wird eine Subshell gestartet.
BTW: Du verwendest Backticks, um die Variable "$zaehler"eins hochzuzählen: zaehler=`expr $zaehler + 1` Das startet ein externes Programm in einer Subshell - wenig performant ;-)
aber portabel.
Siehe unten.
zaehler=$(($zaehler+1)) - und das ohne Subshell ;-) Eine weitere Variante ohne Subshell wäre let zaehler=$zaehler+1
AFAIK alles bash-spezifisch.
Falsch. Arithmetic expansion ist POSIX. Nur Solaris /bin/sh und ash (welche beide nicht POSIX konform sind) haben das nicht.
Woher soll ich wissen, ob die verwendete /bin/sh wirklich POSIX-konform ist? Wenn nicht, wirft das Script Fehlermeldungen... Mir ist jedenfalls wesentlich wohler, wenn oben #!/bin/bash und nicht #!/bin/sh stehen darf ;-) Übrigens: vi markiert so Dinge wie $(...) als Fehler, wenn #!/bin/sh angegeben ist... Da fällt mir gerade ein: SuSE scheint in den init-Scripten eine POSIX-konforme /bin/sh vorauszusetzen, da Ihr da regen Gebrauch von $(...) usw. macht. Was spricht dagegen, einfach /bin/bash reinzuschreiben? Gruß Christian Boltz -- Nix da, es werden bitte "funktionierende Pinguine" gebaut. ;-))) [Gerald Engl in suse-linux]
On Thu, 2003-08-14 at 22:06, Christian Boltz wrote:
Hallo Michael, hallo Jan, hallo Leute,
Am Donnerstag, 14. August 2003 10:30 schrieb Michael Matz:
On Thu, 14 Aug 2003, Jan Trippler wrote:
BTW: Du verwendest Backticks, um die Variable "$zaehler"eins hochzuzählen: zaehler=`expr $zaehler + 1` Das startet ein externes Programm in einer Subshell - wenig performant ;-)
aber portabel.
Siehe unten.
zaehler=$(($zaehler+1)) - und das ohne Subshell ;-) Eine weitere Variante ohne Subshell wäre let zaehler=$zaehler+1
AFAIK alles bash-spezifisch.
Falsch. Arithmetic expansion ist POSIX. Nur Solaris /bin/sh und ash (welche beide nicht POSIX konform sind) haben das nicht.
Woher soll ich wissen, ob die verwendete /bin/sh wirklich POSIX-konform ist?
Indem Du es vorab weisst ;) Im Ernst, ein gängiger Work-Around für derartige Probleme besteht darin vorauszusetzen, dass eine POSIX-konforme Shell verwendet wird und diese anstatt von /bin/sh zu verwenden.
Mir ist jedenfalls wesentlich wohler, wenn oben #!/bin/bash und nicht #!/bin/sh stehen darf ;-) ... was wiederum nicht portabel ist, da es /bin/bash eigentlich nur auf GNU-basierten Systemen gibt.
Auf nicht GNU-Systemen sind /usr/bin/bash, /usr/local/bin/bash oder <someobscurepath>/bin/bash weitaus häufiger anzutreffen. (Auf unseren Nicht-Linux-Boxen liegt die bash z.Zt. z.B. unter /amd/bin/bash) Ein Ausweg aus diesem Problem wäre ein kleines configure-script um das Script herum zu schreiben, dass nach einer bash sucht und die scripte entsprechend umschreibt.
Da fällt mir gerade ein: SuSE scheint in den init-Scripten eine POSIX-konforme /bin/sh vorauszusetzen, da Ihr da regen Gebrauch von $(...) usw. macht. Was spricht dagegen, einfach /bin/bash reinzuschreiben? Init-Scripte werden nur lokal verwendet, deshalb ist unter SuSE sichergestellt, dass /bin/sh == bash == "POSIX-konforme Shell" gilt.
Viel schwerwiegender ist hingegen der Gebrauch von Bash und POSIX-/bin/sh-Konstrukten in Per-User-Scripten (/etc/skel etc.), da User-Homes nicht unbedingt nur unter Linux und schon gar nicht nur lokal verwendet werden (z.B. weil User-Homes via amd/automount/afs transparent in einem heterogenen Netz gemountet werden). Ich weiss nicht, ob sich da bei SuSE was zum Positiven hin geändert hat. Bei älteren SuSE-Versionen lag da jedenfalls einiges im Argen. Ralf
On Fre, 15 Aug 2003 at 09:58 (+0200), Ralf Corsepius wrote: [...]
Auf nicht GNU-Systemen sind /usr/bin/bash, /usr/local/bin/bash oder <someobscurepath>/bin/bash weitaus häufiger anzutreffen. (Auf unseren Nicht-Linux-Boxen liegt die bash z.Zt. z.B. unter /amd/bin/bash)
Hm, auf Nicht-GNU-Systemen existiert nach meiner Erfahrung erstmal überhaupt keine Bash. Sie tut es dann, wenn ein Admin sie haben will und nachinstalliert - sonst gibts sh, ksh und vielleicht noch csh. Jan
On Sat, 2003-08-16 at 02:42, Jan Trippler wrote:
On Fre, 15 Aug 2003 at 09:58 (+0200), Ralf Corsepius wrote: [...]
Auf nicht GNU-Systemen sind /usr/bin/bash, /usr/local/bin/bash oder <someobscurepath>/bin/bash weitaus häufiger anzutreffen. (Auf unseren Nicht-Linux-Boxen liegt die bash z.Zt. z.B. unter /amd/bin/bash)
Hm, auf Nicht-GNU-Systemen existiert nach meiner Erfahrung erstmal überhaupt keine Bash.
Klar ;) Doch * wenn man schon GNU-SW auf Nicht-Linuxen installieren will, installiert man in der Regel auch die Bash. * wenn man auf nicht GNU-Systemen "Bash-Scripte" benutzen will, installiert man dann auch in der Regel die Bash. * wenn man auf nicht GNU-Systemen "POSIX"-Scripte benutzen will, ist Installation der Bash ein relativ einfacher Weg potentielle Probleme zu umgehen. Anderenfalls liegt an Dir herauszufinden, welche der Shells auf deinem Nicht-Linux-System hinreichend "POSIX" oder "Bash"-kompatibel sind, um sie nutzen zu können - Das kann sich im Einzelfall sehr mühselig gestalten.
Sie tut es dann, wenn ein Admin sie haben will und nachinstalliert - sonst gibts sh, ksh und vielleicht noch csh.# Du meinst als Default-User-Login-Shell?
Es gibt die, die ein Sys-Admin als Shell installiert hat - Das können durchaus auch andere sein als sie der Hersteller vorinstalliert. Immer die, die ein Sys-Admin für sinnvoll hält oder auch die, die die User wollen. Beispiel: In unserem Institutsnetz habe ich lange Zeit die tcsh als Login-Shell verwendet, da einerseits Solarismaschinen und csh-Scripte vorherrschend waren, andererseits die tcsh zur csh kompatibel ist, aber auch auf anderen OSen läuft. Im Augenblick verwende ich nur noch die Bash (Auch unter Solaris), da ich vorwiegend unter Linux arbeite und die Solaris /bin/sh und /usr/xpg4/bin/sh-Kompatibilitätsproblem leid bin und sich die SysAdmins den Useranfragen nach der Bash gebeugt haben ;) Ralf
Am Donnerstag, 14. August 2003 00:22 schrieb Christian Boltz:
Hallo Jan, hallo Thomas, hallo Leute,
Hallo Christian, zu gern wuerde ich so fit in Sachen Shell sein, wie Du, Jan uva. :-)
Stark verkürztes Beispiel:
#!/bin/bash zaehler=0
echo -e "a\nb\nc\nd" | { # ^----- hier startet die Subshell while read line do zaehler=`expr $zaehler + 1` done
echo Anzahl: $zaehler
exit } # erst hier endet die Subshell!
Erklärung aus man bash:
{ list; } list is simply executed in the current shell envi- ronment. list must be terminated with a newline or semicolon.
Diese Loesung gefaellt mir! (Laeuft auch schon problemlos)
BTW: Du verwendest Backticks, um die Variable "$zaehler"eins hochzuzählen: zaehler=`expr $zaehler + 1` Das startet ein externes Programm in einer Subshell - wenig performant ;-) Genausogut funktioniert die (vermutlich bash-spezifische) Variante zaehler=$(($zaehler+1)) - und das ohne Subshell ;-) Eine weitere Variante ohne Subshell wäre let zaehler=$zaehler+1
Deine beiden Beispiele sind laut man bash voellig identisch. Leider bin ich nicht selbst drauf gestossen:-( ((expression)) The expression is evaluated according to the rules described below under ARITHMETIC EVALUATION. If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1. This is exactly equivalent to let "expression". Ich habe mir im Laufe der letzten Jahre verschiedene Sachen ausgearbeitet und als Muster abgelegt. Das ersparte mir bisher viel Sucherei. Jetzt kann ich meine Beispiele wieder etwas erweitern:-) Danke! Du zeigst mir oben, wie ich ohne Backticks, also ohne weitere Subshell, rechnen kann. Nach solch einer Variante hatte ich bisher leider nie gesucht - ging ja auch immer nach meinem Muster:-) Wie koennte ich nun auch folgendes besser Loesen?: (vielleicht geht´s ja auch ohne nach cut zu pipen?) read line myvar=`echo "$line" | cut -c 4-11` Wenn ich auch hier die SubShell umgehen koennte, wuerde das einen enormen Geschwindigkeitsvorteil bringen - vermute ich mal. Diese Art Variablenzuweisung laeuft mehrfach in einem umfangreichen case - esac Block. Ich war schon auf der Suche nach sowas wie SrtLen, StrPos, StrMove usw. In dieser Art scheint es jedoch kein Aequivalent in der Shell zu geben - sagt jedenfalls 'bash -c help' und in 'man bash' wurde ich auch nicht fuendig. Vielleicht habe ich ja wieder Tomaten auf den Augen? Besten Dank fuer Eure Bemuehungen! MfG Th. Moritz -- Geisterfahrer sind oft sehr entgegenkommend!
Thomas Moritz schrieb:
Ich habe mir im Laufe der letzten Jahre verschiedene Sachen ausgearbeitet und als Muster abgelegt. Das ersparte mir bisher viel Sucherei. Jetzt kann ich meine Beispiele wieder etwas erweitern:-) Danke! Du zeigst mir oben, wie ich ohne Backticks, also ohne weitere Subshell, rechnen kann. Nach solch einer Variante hatte ich bisher leider nie gesucht - ging ja auch immer nach meinem Muster:-)
Wie koennte ich nun auch folgendes besser Loesen?: (vielleicht geht´s ja auch ohne nach cut zu pipen?)
read line myvar=`echo "$line" | cut -c 4-11`
myvar=$(echo "$line" | cut -c 4-11)
Wenn ich auch hier die SubShell umgehen koennte, wuerde das einen enormen Geschwindigkeitsvorteil bringen - vermute ich mal. Diese Art Variablenzuweisung laeuft mehrfach in einem umfangreichen case - esac Block. Ich war schon auf der Suche nach sowas wie SrtLen, StrPos, StrMove usw. In dieser Art scheint es jedoch kein Aequivalent in der Shell zu geben - sagt jedenfalls 'bash -c help' und in 'man bash' wurde ich auch nicht fuendig. Vielleicht habe ich ja wieder Tomaten auf den Augen?
Schau Dir mal sed und awk genauer an. Oder nimm "wc" für Längenmessungen, "tr" für Ersetzen von Zeichen, ... Falls es das Buch noch gibt, ist "Unix in a Nutshell" (erschienen bei O'Reilly) recht brauchbar. Da stehen Informationen zu den verschiedenen Shells und Ihren eingebauten Kommandos und zu den "Standard"-Unix-Tools (wc,tr,cut,cat,...) drin. CU Dierk -- // Dierk Froehling // aycan Digitalsysteme GmbH // Innere Aumuehlstr. 5 // 97076 Wuerzburg // Germany // phone: +49 (0)931 2 70 40 9-0 // fax: +49 (0)931 2 70 40 9-1 // http://www.aycan.de
On Thu, 2003-08-14 at 15:00, dfroehling wrote:
Thomas Moritz schrieb:
Ich habe mir im Laufe der letzten Jahre verschiedene Sachen ausgearbeitet und als Muster abgelegt. Das ersparte mir bisher viel Sucherei. Jetzt kann ich meine Beispiele wieder etwas erweitern:-) Danke! Du zeigst mir oben, wie ich ohne Backticks, also ohne weitere Subshell, rechnen kann. Nach solch einer Variante hatte ich bisher leider nie gesucht - ging ja auch immer nach meinem Muster:-)
Wie koennte ich nun auch folgendes besser Loesen?: (vielleicht geht´s ja auch ohne nach cut zu pipen?)
read line myvar=`echo "$line" | cut -c 4-11`
myvar=$(echo "$line" | cut -c 4-11)
Worin liegt der Vorteil? Ich darf mal IEEE Std 1003.1-2001 (SUSV3) zitieren: .. Command substitution allows the output of a command to be substituted in place of the command name itself. Command substitution shall occur when the command is enclosed as follows: $(command) or (backquoted version): `command` The shell shall expand the command substitution by executing command in a subshell environment (see Shell Execution Environment ) and replacing the command substitution (the text of command plus the enclosing "$()" or backquotes) with the standard output of the command, removing sequences of one or more <newline>s at the end of the substitution. .. => Ich sehe keinen grundsätzlichen Vorteil. Beide werden von POSIX unterstützt. (Der eigentliche Vorteil von $() gegenüber `` ist die Schachtelbarkeit von $()). Aber, alte Shells (z.B. unter SunOS4) und selbst einige "moderne" Shells (Solaris-5.8, Irix-6.5; vgl. info autoconf -> Portable Shell -> Shell Substitutions)) kennen nur die ``-Variante. D.h. willst Du "nur" POSIX unterstützen, ist es prinzipiell egal, was verwendet wird. Willst Du Portabilität, nimm die Backtick-Variante. BTW, ähnliches gilt für die "Arithmetic Expressions". POSIX kennt $(()), portabel (auch auf alte Systeme) ist `expr <expression>`. Mit anderen Worten: Willst Du ein Bash-Script schreiben, nimm $() und $(()), willst Du ein zu älteren Bourne-Shells kompatibles Script schreiben, nimmt `` und `expr `. Ralf
Ralf Corsepius schrieb:
read line myvar=`echo "$line" | cut -c 4-11`
myvar=$(echo "$line" | cut -c 4-11)
Worin liegt der Vorteil?
Ich darf mal IEEE Std 1003.1-2001 (SUSV3) zitieren: .. Command substitution allows the output of a command to be substituted in place of the command name itself. Command substitution shall occur when the command is enclosed as follows:
$(command) or (backquoted version):
`command` The shell shall expand the command substitution by executing command in a subshell environment (see Shell Execution Environment ) and replacing the command substitution (the text of command plus the enclosing "$()" or backquotes) with the standard output of the command, removing sequences of one or more <newline>s at the end of the substitution.
..
Ist mir neu, mit der subshell bei $(command), aber man lernt nie aus. :-) Hätte ich eigentlich auch sehen müssen, wenn ich meine Buchtipps selber besser befolgen würde. ;-) Dierk -- // Dierk Froehling // aycan Digitalsysteme GmbH // Innere Aumuehlstr. 5 // 97076 Wuerzburg // Germany // phone: +49 (0)931 2 70 40 9-0 // fax: +49 (0)931 2 70 40 9-1 // http://www.aycan.de
On Don, 14 Aug 2003 at 16:01 (+0200), Ralf Corsepius wrote: [`` vs. $()]
=> Ich sehe keinen grundsätzlichen Vorteil. Beide werden von POSIX unterstützt.
(Der eigentliche Vorteil von $() gegenüber `` ist die Schachtelbarkeit von $()).
Auch `` kann man schachteln: deepest_dir=`basename \`dirname $pfad\`` Das geht beliebig tief. Zum Rest ACK. Jan
On Thu, 2003-08-14 at 23:00, Jan Trippler wrote:
On Don, 14 Aug 2003 at 16:01 (+0200), Ralf Corsepius wrote: [`` vs. $()]
=> Ich sehe keinen grundsätzlichen Vorteil. Beide werden von POSIX unterstützt.
(Der eigentliche Vorteil von $() gegenüber `` ist die Schachtelbarkeit von $()).
Auch `` kann man schachteln: deepest_dir=`basename \`dirname $pfad\``
Theoretisch ja, doch beachte \` vs. ` SUSV3 sagt dazu: ... Command substitution can be nested. To specify nesting within the backquoted version, the application shall precede the inner backquotes with backslashes, for example: \`command\` If the command substitution consists of a single subshell, such as: $( (command) ) a conforming application shall separate the "$(" and '(' into two tokens (that is, separate them with white space). ... In der Praxis jedoch gibt es auch hier wieder Fälle in denen es nicht geht. info autoconf deutet dies in einer Randbemerkung zu $() an: ... `$(COMMANDS)' This construct is meant to replace ``COMMANDS`'; they can be nested while this is impossible to do portably with back quotes. ... Mir sind Fälle in denen es nicht geht allerdings auch noch nicht persönlich untergekommen - Ich vermeide sie in der Regel allerdings auch ;) Ralf
Am Donnerstag, 14. August 2003 15:00 schrieb dfroehling: Hallo Dierk,
Wie koennte ich nun auch folgendes besser Loesen?: (vielleicht geht´s ja auch ohne nach cut zu pipen?)
read line myvar=`echo "$line" | cut -c 4-11`
myvar=$(echo "$line" | cut -c 4-11)
Eine Sekunde, nachdem ich die letzte Mail abgeschickt hatte, hat es Klick gemacht:-( Aber leider zu spaet.
Schau Dir mal sed und awk genauer an. Oder nimm "wc" für Längenmessungen, "tr" für Ersetzen von Zeichen, ...
Diese Funktionen sind mir durchaus bekannt. Damit musste ich mich mal herumschlagen, als ich meinen Terminplaner gebastelt habe:-) [ Ja, ich hatte einen Hochzeitstag vergessen... Nun nicht mehr! ]
Falls es das Buch noch gibt, ist "Unix in a Nutshell" (erschienen bei O'Reilly) recht brauchbar. Da stehen Informationen zu den verschiedenen Shells und Ihren eingebauten Kommandos und zu den "Standard"-Unix-Tools (wc,tr,cut,cat,...) drin.
Ich habe vor langer Zeit mal damit angefangen eine html-Seite zu stricken. Leider hatte ich bisher nicht genuegend Zeit und oft auch keine Lust an dem Projekt weiter zu machen. http://www.moritz4you.de/linux/befehle/index.html Sollte ich es doch mal schaffen, dort viele Parameter mit Beispielen einzubringen, dann gibt es fuer mich kein besseres Nachschlagewerk. Was ich selbst geschrieben habe ist dann meist auch im Kopf:-) Vielleicht sollte ich diese Seite mal direkt fuer die Shell fertig machen. Dann wird es auch uebersichtlicher. MfG Th. Moritz -- Geisterfahrer sind oft sehr entgegenkommend!
Hi, On Thu, 14 Aug 2003, Thomas Moritz wrote:
myvar=`echo "$line" | cut -c 4-11`
myvar=$(echo "$line" | cut -c 4-11)
Diese beiden Varianten sind voellig aequivalent und oeffnen beide eine subshell, i.e. sind langsam. Nein nein, der wahre bash Geniesser benutzt deren maechtige String-vermangel-expanders: read line echo ${line:4:$((11-4))} Das ist dann allerdings nicht mehr POSIX, dafuer aber ein Batzen schneller. Erste Zahl offset, zweite Zahl Laenge. Offset ab 0. Ciao, Micha.
Am Donnerstag, 14. August 2003 20:18 schrieb Michael Matz: Hallo Michael,
On Thu, 14 Aug 2003, Thomas Moritz wrote:
myvar=`echo "$line" | cut -c 4-11`
myvar=$(echo "$line" | cut -c 4-11)
Diese beiden Varianten sind voellig aequivalent und oeffnen beide eine subshell, i.e. sind langsam. Nein nein, der wahre bash Geniesser benutzt deren maechtige String-vermangel-expanders:
read line echo ${line:4:$((11-4))}
Das ist dann allerdings nicht mehr POSIX, dafuer aber ein Batzen schneller. Erste Zahl offset, zweite Zahl Laenge. Offset ab 0.
Simmt nicht ganz, da anders gerechnet wird als bei 'cut -c 4-11' ! Folgende Zeile bringt das gewuenschte Ergebnis: echo ${line:3:$((8))} ...also _nach_ dem 3. Zeichen 8 Zeichen ausgeben ! Leider kann ich heute auf Speed nicht mehr testen:-( Besten Dank! MfG Th. Moritz -- Geisterfahrer sind oft sehr entgegenkommend!
Hallo Thomas, hallo Leute, Am Donnerstag, 14. August 2003 14:40 schrieb Thomas Moritz:
Am Donnerstag, 14. August 2003 00:22 schrieb Christian Boltz: zu gern wuerde ich so fit in Sachen Shell sein, wie Du, Jan uva. :-)
Ich habe für diese und meine gestrige Mail auch erst in man bash stöbern müssen ;-) Meist geht es nach dem Motto "Hmmm, IIRC kann das die Bash doch auch, oder? Mal nachsehen..."
BTW: Du verwendest Backticks, um die Variable "$zaehler"eins hochzuzählen: zaehler=`expr $zaehler + 1` Das startet ein externes Programm in einer Subshell - wenig performant ;-) Genausogut funktioniert die (vermutlich bash-spezifische) Variante zaehler=$(($zaehler+1)) - und das ohne Subshell ;-) Eine weitere Variante ohne Subshell wäre let zaehler=$zaehler+1
Deine beiden Beispiele sind laut man bash voellig identisch.
Jepp.
Leider bin ich nicht selbst drauf gestossen:-(
man bash ist ja auch nicht gerade sonderlich übersichtlich ;-)
((expression)) The expression is evaluated according to the rules described below under ARITHMETIC EVALUATION. If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1. This is exactly equivalent to let "expression".
Genau das. zaehler=$[$zaehler+1] funktioniert übrigens auch ;-)
Wie koennte ich nun auch folgendes besser Loesen?: (vielleicht geht´s ja auch ohne nach cut zu pipen?)
read line myvar=`echo "$line" | cut -c 4-11`
Wenn ich auch hier die SubShell umgehen koennte, wuerde das einen enormen Geschwindigkeitsvorteil bringen - vermute ich mal. Diese Art Variablenzuweisung laeuft mehrfach in einem umfangreichen case - esac Block. Ich war schon auf der Suche nach sowas wie
SrtLen,
${#varable}
StrPos,
Gibt es AFAIK nicht (oder ich hab was übersehen), aber es gibt einige andere nette Tricks ;-) cb@tux:~> TEST=1234567890 cb@tux:~> echo ${TEST#*4} 567890 # alles einschließlich der "4" vorne wegschnippeln cb@tux:~> echo ${TEST%5*} 1234 # alles einschließlich der "5" hinten wegschnippeln Es gibt jeweils auch eine Variante mit doppeltem ## bzw. %%. Der Unterschied liegt darin, ob das erste oder letzte Auftreten des Suchmusters berücksichtigt wird. Da das aber bei meinem $TEST sowieso nicht relevant ist, darfst Du das aber selbst in der manpage nachschlagen ;-) cb@tux:~> echo ${TEST/3/_} 12_4567890 Diese Variante arbeitet mit RegEx, so ist z. B. das folgende möglich: cb@tux:~> echo ${TEST/[a58b]/_} 1234_6789 Aber Achtung: Es wird immer nur das erste Vorkommen ersetzt!
StrMove
Sollte sich durch Zurechtschneiden und erneutes Zusammensetzen einer Variable (ggf. über den Umweg mit Hilfsvariablen) emulieren lassen. Allgemeiner Tip: Abschnitt "Parameter Expansion" in man bash ;-) Vielleicht interessiert Dich auch ein (etwas älteres) Script von mir, das einige solcher ${irgendwelchetricks} verwendet. http://tux.boltz.de.vu (Baugrube, nicht erschrecken ;-) -> diffdirs Aber Vorsicht bei der Verwendung dieses Scripts: ich verwende es hauptsächlich zum Ermitteln nötiger Uploads für Webseiten, also nur bei "braven" Dateinamen. Schon ein Leerzeichen im Dateinamen führt vermutlich zu einem Fehler! Für Webseiten ist diese Einschränkung IMHO sogar vorteilhaft (besser in diesem Stadium ein Fehler als im ErrorLog des Webservers ;-) , aber für andere Dateien ist es eben nachteilig. Auch sonst ist das Script wohl nicht ganz fehlerfrei, aber es erfüllt seinen Zweck ;-) Jedenfalls lade ich nachher noch die aktuelle Version hoch, damit wenigstens die längst behobenen Fehler draußen sind ;-) Apropos@all: die folgende Zeile wirbelt in vim das Syntax-Highlighting durcheinander: SUBDIR="${3:${#VERZALT}+1}"; Ist das ein Bug im vim Syntax-Script oder ist diese Zeile nicht einwandfrei? cb@tux:~/bin> vim --version | head -n1 VIM - Vi IMproved 6.1 (2002 Mar 24, compiled Aug 5 2003 13:26:57)
usw. In dieser Art scheint es jedoch kein Aequivalent in der Shell zu geben - sagt jedenfalls 'bash -c help' und in 'man bash' wurde ich auch nicht fuendig. Vielleicht habe ich ja wieder Tomaten auf den Augen?
Könnte sein - ich musste allerdings auch erstmal in man bash nachschlagen: ${parameter:offset} ${parameter:offset:length} Substring Expansion. Expands to up to length char- acters of parameter starting at the character spec- ified by offset. If length is omitted, expands to the substring of parameter starting at the charac- ter specified by offset. length and offset are arithmetic expressions (see ARITHMETIC EVALUATION below). length must evaluate to a number greater than or equal to zero. If offset evaluates to a number less than zero, the value is used as an off- set from the end of the value of parameter. If parameter is @, the result is length positional parameters beginning at offset. If parameter is an array name indexed by @ or *, the result is the length members of the array beginning with ${param- eter[offset]}. Substring indexing is zero-based unless the positional parameters are used, in which case the indexing starts at 1. Somit auf Deinen Bedarf übertragen:
read line myvar=`echo "$line" | cut -c 4-11`
cb@tux:~> TEST=1234567890abcdefghijklmnopqrstuvwxyz cb@tux:~> echo $TEST | cut -c 4-11 # zum Vergleich 4567890a cb@tux:~> echo ${TEST:3:8} 4567890a Die gesuchte Befehlszeile ist also myvar=${line:3:8} Dann mal viel Spaß beim Beschleunigen des Scripts! Sonst noch irgendwelche Subshells? ;-) Gruß Christian Boltz PS: Ich schätze mal, dass ${var:offset:length} und sonstige Spezialitäten mal wieder bash- oder POSIX-spezifisch sind, also nicht mit (jeder) /bin/sh läuft ;-) -- [Virenscanner] Stattdessen gehört auf einen Windows-Arbeitsplatz ein guter, selbstaktualisierender lokaler Scanner, der die Windows-Kiste so richtig schön langsam beim Öffnen von Dateien macht, um den Windows-Anwender zu motivieren, auf Linux umzusteigen. [Kristian Koehntopp in suse-linux]
participants (6)
-
Christian Boltz
-
dfroehling
-
Jan.Trippler@t-online.de
-
Michael Matz
-
Ralf Corsepius
-
Thomas Moritz