Umkehren des Inhalts einer Variablen durch Wörter

13

Also, wenn ich eine Variable habe

VAR='10 20 30 40 50 60 70 80 90 100'

und es wiedergeben

echo "$VAR"
10 20 30 40 50 60 70 80 90 100

Weiter unten im Skript muss ich jedoch die Reihenfolge dieser Variablen umkehren, damit sie so etwas wie angezeigt wird

echo "$VAR" | <code to reverse it>
100 90 80 70 60 50 40 30 20 10

Ich habe versucht, rev zu verwenden, und es hat buchstäblich alles umgekehrt, so dass es herauskam als

echo "$VAR" | rev
001 09 08 07 06 05 04 03 02 01
Alistair Hardy
quelle
2
In den Antworten auf diese ähnliche Frage finden Sie möglicherweise einige nützliche Vorschläge: Wie lese ich eine IP-Adresse rückwärts? (müssen nur für Leerzeichen anstelle der Zeiträume einstellen)
Stahlfahrer

Antworten:

21

Auf GNU-Systemen lautet die Umkehrung von cat tac:

$ tac -s" " <<< "$VAR "            # Please note the added final space.
100 90 80 70 60 50 40 30 20 10
Ipor Sircer
quelle
Prost! Das hat funktioniert. Ich muss noch viel lernen
Alistair Hardy
@JhonKugelman Ohne das Echo hat die Ausgabe einen zusätzlichen Zeilenumbruch, der dem folgenden entspricht:echo $'100 \n90 80 70 60 50 40 30 20 10'
sorontar
In der aktuellen tac -s" " <<< "$VAR "Version wird weiterhin eine führende Leerzeile eingefügt, ein abschließendes Leerzeichen hinzugefügt und das abschließende Zeilenumbruchzeichen weggelassen (wie printf '\n%s ' "$reversed_VAR"erwartet printf '%s\n' "$reversed_VAR")
Stéphane Chazelas,
@don_crissti Die ursprüngliche Antwort: echo $(tac -s" " <<< $VAR)hat kein nachgestelltes Leerzeichen, da die $(…)nachgestellten Zeilen und Leerzeichen entfernt werden, wenn das IFS sie hat. Meine Lösung wird bald korrigiert, danke.
Sorontar
6

Für eine kurze Liste und in einer Variablen ist die Shell selbst die schnellste Lösung:

var="10 20 30 40 50 60 70 80 90 100"
set -- $var; unset a 
for ((i=$#;i>0;i--)); do
    printf '%s%s' "${a:+ }" "${!i}"
    a=1
done
echo

Ausgabe:

100 90 80 70 60 50 40 30 20 10 

Hinweis: Die Aufteilungsoperation in set -- $varist für die hier verwendete Zeichenfolge ungefährlich. Dies ist jedoch nicht der Fall, wenn die Zeichenfolge Platzhalterzeichen ( *, ?oder []) enthält. Es kann mit vermieden werdenset -f vor dem Aufteilen . Der gleiche Hinweis gilt auch für die folgenden Lösungen (sofern nicht anders angegeben).

Oder, wenn Sie eine andere Variable mit der umgekehrten Liste festlegen möchten:

var="10 20 30 40 50 60 70 80 90 100"
set -- $var
for i; do out="$i${out:+ }$out"; done
echo "$out"

Oder nur die Positionsparameter verwenden.

var="10 20 30 40 50 60 70 80 90 100"
set -- $var
a=''
for    i
do     set -- "$i${a:+ $@}"
       a=1
done
echo "$1"

Oder verwenden Sie nur eine Variable (die var selbst sein kann):
Hinweis: Diese Lösung ist weder von Globing noch von IFS betroffen.

var="10 20 30 40 50 60 70 80 90 100"

a=" $var"
while [[ $a ]]; do
    printf '<%s>' "${a##* }"
    var="${a% *}"
done
Sorontar
quelle
2

awk zur Rettung

$ var='10 20 30 40 50 60 70 80 90 100'

$ echo "$var" | awk '{for(i=NF; i>0; i--) printf i==1 ? $i"\n" : $i" "}'
100 90 80 70 60 50 40 30 20 10


mit perlfreundlicher Genehmigung Wie lese ich eine IP-Adresse rückwärts? geteilt von @steeldriver

$ echo "$var" | perl -lane '$,=" "; print reverse @F'
100 90 80 70 60 50 40 30 20 10


Oder mit sich bashselbst, indem Sie die Zeichenfolge in ein Array konvertieren

$ arr=($var)    
$ for ((i=${#arr[@]}-1; i>=0; i--)); do printf "${arr[i]} "; done; echo
100 90 80 70 60 50 40 30 20 10 
Sundeep
quelle
2

Sie können Bash-Funktionen wie Arrays verwenden, um dies vollständig in Bearbeitung zu tun:

#!/bin/bash
VAR="10 20 30 40 50 60 70 80 90 100"
a=($VAR); rev_a=()
for ((i=${#a[@]}-1;i>=0;i--)); do rev_a+=( ${a[$i]} ); done; 
RVAR=${rev_a[*]}; 

Dies sollte der schnellste Weg sein, vorausgesetzt, dass $ VAR nicht sehr groß ist.

PSkocik
quelle
Dies gilt jedoch nicht für Variablen, deren Wert Platzhalterzeichen (wie VAR='+ * ?') enthält. Verwenden Sie set -o nogloboder set -f, um das zu beheben. Generell ist es eine gute Idee, den Wert $IFSund den Status des Globbing zu berücksichtigen, wenn Sie den Operator split + glob verwenden. Nein, dass es wenig sinnvoll ist, diesen Operator zu verwenden rev_a+=( ${a[$i]} ), rev_a+=( "${a[$i]}" )wäre viel sinnvoller.
Stéphane Chazelas
1
printf "%s\n" $var | tac | paste -sd' '
100 90 80 70 60 50 40 30 20 10
Costas
quelle
1

Verwenden Sie hier ein bisschen Python-Magie:

bash-4.3$ VAR='10 20 30 40 50 60 70 80 90 100'
bash-4.3$ python -c 'import sys;print " ".join(sys.argv[1].split()[::-1])'  "$VAR" 
100 90 80 70 60 50 40 30 20 10

Die Funktionsweise ist recht einfach:

  • Wir bezeichnen uns "$VAR"als zweiten Befehlszeilenparameter sys.argv[1](der erste Parameter mit der -cOption ist immer genau das - sich -cselbst. Beachten Sie die Anführungszeichen, um "$VAR"zu verhindern, dass die Variable selbst in einzelne Wörter aufgeteilt wird (dies geschieht durch Shell, nicht durch Python).
  • Als nächstes verwenden wir die .split()Methode, um sie in eine Liste von Zeichenketten aufzuteilen
  • [::-1]Teil ist nur erweiterte Slice-Syntax , um die Liste umzukehren
  • zum schluss fassen wir alles wieder zu einer reihe zusammen " ".join()und drucken aus

Aber diejenigen, die keine großen Python-Fans sind, können AWKim Wesentlichen dasselbe tun: Aufteilen und Array in umgekehrter Reihenfolge drucken:

 echo "$VAR" | awk '{split($0,a);x=length(a);while(x>-1){printf "%s ",a[x];x--};print ""}'
100 90 80 70 60 50 40 30 20 10 
Sergiy Kolodyazhnyy
quelle
1
oder etwas saubererpython3 -c 'import sys;print(*reversed(sys.argv[1:]))' $VAR
iruvar
@iruvar, nicht Cleaner , ruft den split + glob-Operator der Shell auf, ohne sicherzustellen, dass er $IFSdie richtigen Zeichen enthält, und ohne den glob-Teil zu deaktivieren.
Stéphane Chazelas
@ StéphaneChazelas, das stimmt - die Python-Portion ist allerdings immer noch sauberer (IMHO). Zurück zu den "$VAR"Renditenpython3 -c 'import sys;print(*reversed(sys.argv[1].split()))' "$VAR"
iruvar
1

zsh:

print -r -- ${(Oa)=VAR}

$=VARspaltet sich $VARauf $IFS. (Oa)ordnet die resultierende Liste in umgekehrter Reihenfolge an. print -r --(wie in ksh), genauso wie echo -E -( zshspezifisch) eine zuverlässige Version vonecho : druckt seine Argumente so, wie sie durch ein Leerzeichen getrennt und durch eine neue Zeile abgeschlossen sind.

Wenn Sie nur nach Leerzeichen und nicht nach den darin $IFSenthaltenen Elementen (Leerzeichen, Tabulator, Zeilenvorschub, Null) teilen möchten, weisen Sie entweder Leerzeichen zu $IFSoder verwenden Sie eine explizite Aufteilung wie folgt :

print -r -- ${(Oas: :)VAR}

So sortieren Sie in umgekehrter numerischer Reihenfolge:

$ VAR='50 10 20 90 100 30 60 40 70 80'
$ print -r -- ${(nOn)=VAR}
100 90 80 70 60 50 40 30 20 10

POSIXly (so würde es auch funktionieren bash):

Mit eingebauter Shell (außer printfin einigen Shells) nur Mechanismen (besser für Variablen mit einem kurzen Wert):

unset -v IFS # restore IFS to its default value of spc, tab, nl
set -o noglob # disable glob
set -- $VAR # use the split+glob operator to assign the words to $1, $2...

reversed_VAR= sep=
for i do
  reversed_VAR=$i$sep$reversed_VAR
  sep=' '
done
printf '%s\n' "$reversed_VAR"

Mit awk(besser für große Variablen, insbesondere mit bash, aber bis zur Grenze der Größe der Argumente (oder eines einzelnen Arguments)):

 awk '
   BEGIN {
     n = split(ARGV[1], a);
     while (n) {printf "%s", sep a[n--]; sep = " "}
     print ""
   }' "$VAR"
Stéphane Chazelas
quelle
0

Inelegante POSIX- Softwaretools einzeilig :

echo `echo $VAR | tr ' ' '\n' | sort -nr`
agc
quelle