Invertire i contenuti di una variabile con le parole


13

Quindi se ho una variabile

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

ed echo

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

Tuttavia, più in basso nello script ho bisogno di invertire l'ordine di questa variabile in modo che mostri qualcosa di simile

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

Ho provato a usare rev e ha letteralmente invertito tutto così è uscito come

echo "$VAR" | rev
001 09 08 07 06 05 04 03 02 01

2
Potresti trovare alcuni suggerimenti utili nelle risposte a questa domanda simile: Come leggere un indirizzo IP al contrario? (
Ho

Risposte:


21

Sui sistemi GNU, il contrario di cat è tac:

$ tac -s" " <<< "$VAR "            # Please note the added final space.
100 90 80 70 60 50 40 30 20 10

Saluti! Questo ha funzionato. Ho ancora molto da imparare
Alistair Hardy,

@JhonKugelman Senza l'eco, l'output ha una nuova riga aggiuntiva, equivalente a questa:echo $'100 \n90 80 70 60 50 40 30 20 10'
sorontar

Con la tac -s" " <<< "$VAR "versione corrente , inserisce ancora una riga vuota iniziale, aggiunge uno spazio finale e omette il carattere di nuova riga finale (come se printf '\n%s ' "$reversed_VAR"invece del previsto printf '%s\n' "$reversed_VAR")
Stéphane Chazelas

@don_crissti La risposta originale: echo $(tac -s" " <<< $VAR)non ha uno spazio finale perché $(…)rimuove nuove righe e spazi finali se l'IFS li ha. La mia soluzione verrà corretta presto, grazie.
sorontar

6

Per un breve elenco, e in una variabile, la shell stessa è la soluzione più veloce:

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

Produzione:

100 90 80 70 60 50 40 30 20 10 

Nota: l'operazione di scissione set -- $varè sicuro per la stringa esatta usata qui, ma non sarà così se la stringa contiene caratteri jolly ( *, ?o []). set -fSe necessario, può essere evitato prima della divisione. La stessa nota è valida anche per le seguenti soluzioni (tranne dove indicato).

Oppure, se si desidera impostare un'altra variabile con l'elenco inverso:

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

O usando solo i parametri posizionali.

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"

O usando solo una variabile (che può essere var stessa):
Nota: questa soluzione non è influenzata dal globing né dall'IFS.

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

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

2

awk Al salvataggio

$ 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


con perl, cortesia Come leggere un indirizzo IP al contrario? condiviso da @steeldriver

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


Oppure, con bashse stesso convertendo la stringa in array

$ 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 

2

Puoi usare le funzionalità di bash come gli array per farlo interamente in-process:

#!/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[*]}; 

Questo dovrebbe essere il modo più veloce per farlo a condizione che $ VAR non sia molto grande.


Ciò tuttavia non si estende alle variabili il cui valore contiene caratteri jolly (come VAR='+ * ?'). Usa set -o noglobo set -fper risolvere il problema. Più in generale, è una buona idea considerare il valore $IFSe lo stato del globbing quando si utilizza l'operatore split + glob. No, non ha molto senso usare quell'operatore rev_a+=( ${a[$i]} ), rev_a+=( "${a[$i]}" )avrebbe molto più senso.
Stéphane Chazelas,

1
printf "%s\n" $var | tac | paste -sd' '
100 90 80 70 60 50 40 30 20 10

1

Usa un po 'di magia Python qui:

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

Il modo in cui funziona è abbastanza semplice:

  • ci riferiamo al "$VAR"secondo paramenter della riga di comando sys.argv[1](il primo parametro con -copzione è sempre proprio questo - -cstesso. Nota la citazione intorno "$VAR"per evitare di dividere la variabile stessa in singole parole (cosa che ha fatto con la shell, non con pitone)
  • Quindi utilizziamo il .split()metodo per suddividerlo in un elenco di stringhe
  • [::-1]parte è solo una sintassi estesa della sezione per ottenere il contrario dell'elenco
  • infine uniamo di nuovo tutto in un'unica stringa con " ".join()e stampiamo

Ma quelli che non sono grandi fan di Python, potremmo usare AWKessenzialmente la stessa cosa: dividere e stampare array al contrario:

 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 

1
o un po 'più pulitopython3 -c 'import sys;print(*reversed(sys.argv[1:]))' $VAR
iruvar

@iruvar, non più pulito , che invocherebbe l'operatore split + glob della shell senza assicurarsi che $IFScontenga i caratteri corretti e senza disabilitare la parte glob.
Stéphane Chazelas,

@ StéphaneChazelas, abbastanza vero - la porzione di pitone è comunque più pulita (IMHO). Quindi, tornando ai "$VAR"rendimentipython3 -c 'import sys;print(*reversed(sys.argv[1].split()))' "$VAR"
iruvar

1

zsh:

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

$=VARdivide $VARsu $IFS. (Oa)ordina l'elenco risultante in ordine inverso di array. print -r --(come in ksh), uguale a echo -E -( zshspecifico) è una versione affidabile diecho : stampa i suoi argomenti come è separato dallo spazio, terminato da newline.

Se vuoi dividere solo sullo spazio e non su ciò che $IFScontiene (spazio, tab, newline, nul per impostazione predefinita), assegna lo spazio $IFSo usa una suddivisione esplicita come:

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

Per ordinare in ordine numerico inverso:

$ 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 (quindi funzionerebbe anche con bash):

Solo con printfmeccanismi di shell incorporati (tranne che in alcune shell) (meglio per variabili con un valore breve):

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"

Con awk(meglio per variabili di grandi dimensioni, in particolare con bash, ma fino al limite della dimensione degli argomenti (o di un singolo argomento)):

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

0

Strumenti software POSIX ineleganti one-liner:

echo `echo $VAR | tr ' ' '\n' | sort -nr`
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.