Membalik isi variabel dengan kata-kata


13

Jadi jika saya punya variabel

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

dan gema itu

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

Namun, lebih jauh ke bawah skrip saya perlu membalik urutan variabel ini sehingga terlihat seperti sesuatu

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

Saya mencoba menggunakan rev dan secara harfiah membalikkan semuanya sehingga keluar sebagai

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

2
Anda dapat menemukan beberapa saran yang berguna dalam jawaban untuk pertanyaan serupa ini: Bagaimana cara membaca alamat IP secara terbalik? (Hanya perlu menyesuaikan pemisah ruang di tempat periode)
steeldriver

Jawaban:


21

Pada sistem GNU, kebalikan dari cat adalah tac:

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

Bersulang! Itu berhasil. Saya masih harus banyak belajar
Alistair Hardy

@JhonKugelman Tanpa gema, output memiliki baris baru tambahan, setara dengan ini: echo $'100 \n90 80 70 60 50 40 30 20 10'
sorontar

Dengan tac -s" " <<< "$VAR "versi saat ini, masih menyisipkan baris kosong terkemuka, menambahkan spasi tambahan dan menghilangkan karakter baris baru tertinggal (seolah-olah printf '\n%s ' "$reversed_VAR"bukannya diharapkanprintf '%s\n' "$reversed_VAR" )
Stéphane Chazelas

@don_crissti Jawaban asli: echo $(tac -s" " <<< $VAR) tidak memiliki spasi tambahan karena $(…)menghilangkan spasi tambahan baris baru dan spasi jika IFS memilikinya. Solusi saya akan segera diperbaiki, terima kasih.
sorontar

6

Untuk daftar pendek, dan dalam sebuah variabel, shell itu sendiri adalah solusi tercepat:

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

Keluaran:

100 90 80 70 60 50 40 30 20 10 

Catatan: operasi pemisahan di set -- $varaman untuk string yang tepat digunakan di sini, tetapi tidak akan jadi jika string berisi karakter wildcard ( *, ?atau []). Ini dapat dihindari dengan set -fsebelum perpecahan, jika diperlukan. Catatan yang sama juga berlaku untuk solusi berikut (kecuali jika disebutkan).

Atau, jika Anda ingin mengatur beberapa variabel lain dengan daftar terbalik:

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

Atau hanya menggunakan parameter posisi.

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"

Atau hanya menggunakan satu variabel (yang mungkin var sendiri):
Catatan: Solusi ini tidak terpengaruh oleh globing atau 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 untuk menyelamatkan

$ 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


with perl, courtesy Bagaimana cara membaca alamat IP secara terbalik?dibagikan oleh @steeldriver

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


Atau, dengan bashsendirinya dengan mengubah string menjadi 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

Anda dapat menggunakan fitur bash seperti array untuk melakukannya sepenuhnya dalam proses:

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

Ini seharusnya tentang cara tercepat untuk melakukannya asalkan $ VAR tidak terlalu besar.


Namun itu tidak meluas ke variabel yang nilainya berisi karakter wildcard (seperti VAR='+ * ?'). Gunakan set -o noglobatau set -funtuk memperbaikinya. Secara umum, itu ide yang baik untuk mempertimbangkan nilai $IFSdan keadaan globbing ketika menggunakan operator split + glob. Tidak, tidak masuk akal untuk menggunakan operator itu rev_a+=( ${a[$i]} ), rev_a+=( "${a[$i]}" )akan lebih masuk akal.
Stéphane Chazelas

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

1

Gunakan sedikit sihir Python di sini:

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

Cara kerjanya cukup sederhana:

  • kita sebut "$VAR"sebagai paramenter baris perintah kedua sys.argv[1](parameter pertama dengan -copsi selalu hanya itu - -citu sendiri. Perhatikan kutipan di sekitar "$VAR"untuk mencegah pemisahan variabel itu sendiri menjadi kata-kata individual (yang hisi dilakukan oleh shell, bukan python)
  • Selanjutnya kita menggunakan .split()metode, untuk memecahnya menjadi daftar string
  • [::-1]Bagian ini hanya memperpanjang sintaks slice untuk mendapatkan kebalikan dari daftar
  • akhirnya kami menggabungkan semuanya kembali menjadi satu string dengan " ".join()dan mencetak

Tetapi mereka yang bukan penggemar Python besar, bisa kita gunakan AWKuntuk melakukan hal yang pada dasarnya sama: split, dan print array secara terbalik:

 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
atau agak bersihpython3 -c 'import sys;print(*reversed(sys.argv[1:]))' $VAR
iruvar

@iruvar, bukan pembersih , yang akan memanggil operator glob + split shell tanpa memastikan $IFSberisi karakter yang tepat dan tanpa menonaktifkan bagian glob.
Stéphane Chazelas

@ StéphaneChazelas, cukup benar - porsi python masih bersih (IMHO) sekalipun. Jadi, kembali ke "$VAR"hasilpython3 -c 'import sys;print(*reversed(sys.argv[1].split()))' "$VAR"
iruvar

1

zsh:

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

$=VARmembagi $VARpada $IFS. (Oa)memesan daftar yang dihasilkan dalam urutan array terbalik. print -r --(seperti dalam ksh), sama dengan echo -E -( zshspesifik) adalah versi yang dapat diandalkanecho : mencetak argumennya sebagai-dipisahkan oleh ruang, diakhiri oleh baris baru.

Jika Anda ingin membagi pada ruang saja, dan bukan pada apa pun yang $IFSberisi (spasi, tab, baris baru, nul secara default), tetapkan ruang untuk $IFS, atau gunakan pemisahan eksplisit seperti:

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

Untuk mengurutkan dalam urutan angka terbalik:

$ 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 (demikian juga akan bekerja dengan bash ):

Hanya dengan printfmekanisme builtin shell (kecuali untuk beberapa shell) (lebih baik untuk variabel dengan nilai pendek):

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"

Dengan awk(lebih baik untuk variabel besar, terutama dengan bash, tetapi hingga batas ukuran argumen (atau argumen tunggal)):

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

0

Janggal POSIX perangkat lunak satu-liner:

echo `echo $VAR | tr ' ' '\n' | sort -nr`
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.