ubuntuusers.de

Zuweisung mittels Command Substitution

Status: Ungelöst | Ubuntu-Version: Xubuntu 22.04 (Jammy Jellyfish)
Antworten |

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17612

Wohnort: Berlin

Hallo!

Ich dachte, ich könne mit einer Commandsubstitution (i.f. CS) eine Zuweisung aus einer Datei schneiden - hier ein Demo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
t530:~/proj/mini/forum 🐧> echo "net=77.77.77 
> echo foobar" > test.rc 
t530:~/proj/mini/forum 🐧> cat test.rc 
net=77.77.77 
echo foobar
t530:~/proj/mini/forum 🐧> head -1 test.rc 
net=77.77.77 
t530:~/proj/mini/forum 🐧> $(head -1 test.rc)
net=77.77.77: Befehl nicht gefunden.
t530:~/proj/mini/forum 🐧> tail -1 test.rc 
echo foobar
t530:~/proj/mini/forum 🐧> $(tail -1 test.rc)
foobar

Wie man sieht: Das echo-Kommando wird klaglos ausgeführt, die Zuweisung nicht (Befehl nicht gefunden).

Kann jmd. erklären, wieso die Zuweisung einen Fehler provoziert?

Ach - wie so oft, wenn man den Fehler oder das Problem en detail zu erklären versucht, kommt man selbst drauf.

Hier aber auch nur fast.

Auszug aus der Bashmanpage, Abschnitt CS

1
2
   Command Substitution
       Command substitution allows the output of a command to replace the command name.  (...)

Soweit, so schön. Der Output von head -1 test.rc ist net=77.77.77. Noch sind wir im grünen Bereich.

1
2
       Bash performs the expansion by executing command in a subshell environment and replacing the command substi-
       tution  with  the standard output of the command, with any trailing newlines deleted. (...)

"Subshell environment" läutet eine Alarmglocke, aber das Subshell-Env. ersetzt den Befehl head -1 test.rc mit "net=77.77.77" - problematisch wäre, wenn der Befehl "net=77.77.77" in einer Subshell liefe, weil er dann für die aufrufende Shell ohne Wirkung bliebe. Aber hier ist der Output ja, wie bei dem Gegenbeispiel mit "echo", wie man sieht, natürlich sichtbar; das ist ja der Witz an CS.

Also die Anweisung "echo foobar" wird, wie erwartet ausgeführt, aber "net=77..." nicht, obwohl es doch beides Anweisungen sind.

Kann das jmd. erklären?

Rationale: Man ergänzt ja oft die .bashrc um neue Funktionen, Aliase und Shellvariablen. Ich habe dann immer Skrupel die .bashrc neu zu sourcen, weil vielleicht steht da ja irgendwo etwas wie "PATH=$PATH:~/bin", und wenn das 10x aufgerufen wird, dann hat man ":~/bin:~/bin:~/bin:~/bin:~/bin:~/bin:~/bin:~/bin:~/bin" (nicht nachgezählt) im Pfad stehen, was nicht schön ist, auch nicht wirklich problematisch, aber wenn das jmd. sieht wird es doch ein Hochziehen der Augenbrauen beim Betrachter womöglich triggern.

Workaround:

Workarounds interessieren mich nicht. Dann tippe ich halt von Hand "net=tralala" ein oder gebe es aus und markiere es mit der Maus, wenn es sehr viel länger ist und fingerbrechende Sonderzeichenkombinationen enthält. Höchstens elegante oder schnuckelige Workarounds interessieren mich.

Dies hier wäre ein Workaround, der funktioniert und dessen Seiteneffekte mir keine Sorgenfalten bereiten, denn selbst wenn ich eine Subshell aufrufe, verwendet diese wahrscheinlich nicht die Variable "net" und wenn doch, dann wohl in 99% der Fälle genau in dem Sinne.

1
export $(head -1 test.rc)

Aber dass es mit export klappt und ohne nicht finde ich dennoch erklärungsbedürftig. Gut, "export" ist ein Befehl/Programm und "net" ist es nicht, "net=" bzw. "net=something" aber ist m.E. ein Befehl, wenn auch kein Programm und keine Funktion.

shiro Team-Icon

Supporter

Anmeldungsdatum:
20. Juli 2020

Beiträge: 1261

Gut, "export" ist ein Befehl/Programm und "net" ist es nicht ...

Jetzt willst du für deine Faulheit auch noch belohnt werden. Eine Variablen-Zuweisung erfolgt "normalerweise" über "declare" oder "typeset". Dies kann man in etlichen Fällen aus Faulheit weglassen. Du hast jetzt einen gefunden, wo du schon korrekt sein solltest.

Dass ein "export" funktioniert ist klar, da es eine "declare -x" Abkürzung ist. Du kannst aber auch "eval" verwenden.

Wenn du in deiner Datei statt "net=77.77.77" besser "declare net=77.77.77" schreibst, klappt es auch mit dem "$(head -1 test.rc)".

user_unknown

(Themenstarter)
Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17612

Wohnort: Berlin

shiro schrieb:

Gut, "export" ist ein Befehl/Programm und "net" ist es nicht ...

Jetzt willst du für deine Faulheit auch noch belohnt werden.

Fehlt hier ein Smilie oder soll das ein Affront sein?

Nicht, dass ich keine ausgeprägt faule Seite hätte, aber selbst wenn könnte man allenfalls bekritteln, dass ich mit meiner Faulheit hier durchkommen will, nicht aber, dass ich für sie belohnt werden will. Eine Belohnung wäre ja ein Extra obendrauf.

Eine Variablen-Zuweisung erfolgt "normalerweise" über "declare" oder "typeset".

Das kann man sicherlich in einer offiziellen Doku nachlesen. In der Bash - ich war nicht faul - konnte ich nichts dazu finden, habe aber auch nur oberflächlich gesucht (war also vielleicht doch faul).

Dies kann man in etlichen Fällen aus Faulheit weglassen. Du hast jetzt einen gefunden, wo du schon korrekt sein solltest.

Soso.

Ich bin ja faul, aber bekannter als für meine Faulheit bin ich vielleicht für hartnäckige Rechhaberei; also nicht faul habe ich mich auf eine empirische Untersuchung eingelassen und mit folgendem Befehl das /etc-Verzeichnis gescannt:

1
sudo find /etc -type f -executable -exec ./isScript.sh {} ";" -exec egrep "=|typedef|declare" {} ";" | wc -l

welcher sich in seiner Gänze erst erschließt, wenn man ./isScript.sh gesehen hat, so dass ich es niemandem vorenthalten will:

1
2
3
cat ./isScript.sh 
#!/bin/sh
file $1 | grep -q 'script,'

Das Ergebnis ist 2322 und für die gleiche Suche ohne "typedef" ist das Ergebnis ebenfalls 2322 - womöglich weil niemand "typedef", sondern alle "declare" benutzen, aber oh Schock, ohne "declare" findet das Kommando auch 2322 Zeilen, ohne "=" jedoch 0, in Worten Null, zero, nada.

Es sind, das soll nicht verschwiegen werden, eine Menge falsch positive "=" in den Suchresultaten. Ich empfehle sich ein eigenes Bild zu machen, in dem man die Pipe durch "wc" weglässt, und stattdessen einen Pager einsetzt.

Dass ein "export" funktioniert ist klar, da es eine "declare -x" Abkürzung ist. Du kannst aber auch "eval" verwenden.

Ein guter Einwand. Ein export findet sich immerhin 67 mal, mutmaßlich um Variablen an Subshells zu vererben - nicht aus Gründen des guten Stils, so dass meine Hypothese, dass name=VALUE sehr viel normaler ist, als die Verwendung von declare oder typeset, ohne dass ich normal in distanzierende Anführungsstriche setzen müsste, hiermit in die Welt gesetzt sei.

Das Schlüsselwort eval findet sich 27 Mal, davon über die Hälfte der Treffer in /etc/postfix/post-install. Würde aber klappen und wäre ein Workaround.

Wenn du in deiner Datei statt "net=77.77.77" besser "declare net=77.77.77" schreibst, klappt es auch mit dem "$(head -1 test.rc)".

Danke.

Das wäre ein anderer Workaround, keine Erklärung dafür, wieso es ohne Boilerplate nicht klappt.

Antworten |