Bir değişkenin içeriğini kelimelerle ters çevirme


13

Eğer bir değişkenim varsa

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

ve yankılanır

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

Ancak, daha fazla komut dosyası aşağı gibi bir değişken olarak gösterir bu değişkenin sırasını tersine gerekir

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

Rev'i kullanmayı denedim ve kelimenin tam anlamıyla her şeyi tersine çevirdi.

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

2
Bu benzer sorunun yanıtlarında bazı yararlı öneriler bulabilirsiniz: Bir IP adresi geriye doğru nasıl okunur? (noktaların yerine boşluk ayırıcılarını ayarlamanız yeterlidir)
12

Yanıtlar:


21

GNU sistemlerinde, kedinin tersi tac:

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

Şerefe! Hile yaptı. Hala öğrenecek çok şeyim var
Alistair Hardy

@JhonKugelman Yankı olmadan, çıkışın buna eşdeğer yeni bir satırı daha vardır:echo $'100 \n90 80 70 60 50 40 30 20 10'
sorontar

Mevcut tac -s" " <<< "$VAR "sürümle birlikte, önde gelen bir boş satır ekler, bir boşluk ekler ve sondaki satırsonu karakterini atlar (sanki printf '\n%s ' "$reversed_VAR"beklenen gibi printf '%s\n' "$reversed_VAR")
Stéphane Chazelas

@don_crissti Orijinal yanıt: echo $(tac -s" " <<< $VAR)sondaki boşluk içermez, çünkü IFS'de $(…)varsa yeni satırları ve boşlukları kaldırır. Çözümüm yakında düzeltilecek, teşekkürler.
sorontar

6

Kısa bir liste ve bir değişken için kabuğun kendisi en hızlı çözümdür:

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

Çıktı:

100 90 80 70 60 50 40 30 20 10 

Not: bölme işlemi set -- $varburada kullanılan tam dize için güvenlidir, ancak dize joker karakterler ( *, ?veya []) içeriyorsa böyle olmaz . set -fGerekirse, ayrılmadan önce önlenebilir . Aynı not aşağıdaki çözümler için de geçerlidir (belirtilen durumlar hariç).

Veya ters listeyle başka bir değişken ayarlamak istiyorsanız:

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

Veya sadece konumsal parametreleri kullanarak.

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"

Veya yalnızca bir değişken (değişkenin kendisi olabilir) kullanarak:
Not: Bu çözüm, globing veya IFS'den etkilenmez.

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

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

2

awk kurtarmak için

$ 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


ile perl, nezaket Nasıl bir IP adresi geriye doğru okumak için? @steeldriver tarafından paylaşıldı

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


Veya bashdizeyi diziye dönüştürerek kendisi

$ 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

Tamamen işlem yapmak için diziler gibi bash özelliklerini kullanabilirsiniz:

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

Bu, $ VAR'ın çok çok büyük olmaması şartıyla, bunu yapmanın en hızlı yolu hakkında olmalıdır.


Ancak bu, değeri joker karakterler (değişken) içeren değişkenleri kapsamaz VAR='+ * ?'. Bunu düzeltmek için set -o noglobveya set -fdüğmesini kullanın . Daha genel olarak, $IFSsplit + glob operatörünü kullanırken globbingin değerini ve durumunu dikkate almak iyi bir fikirdir . Hayır o operatörünü kullanmak için çok az mantıklı rev_a+=( ${a[$i]} ), rev_a+=( "${a[$i]}" )çok daha mantıklıdır.
Stéphane Chazelas

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

1

Burada biraz Python büyüsü kullanın:

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

Bunun çalışma şekli oldukça basittir:

  • biz "$VAR"ikinci komut satırı paramenter olarak adlandırır sys.argv[1]( -cseçeneği ile ilk parametre her zaman sadece - -ckendisidir. "$VAR"Değişkenin kendisini tek tek kelimelere (pisi değil hisi tarafından yapılan hisi) bölmeyi önlemek için alıntıya dikkat edin
  • Daha .split()sonra, bir dizeler listesine bölmek için yöntem kullanıyoruz
  • [::-1]bölüm listenin tersine almak için sadece genişletilmiş dilim sözdizimi
  • Sonunda her şeyi bir dizeye geri ekleyip " ".join()yazdırıyoruz

Ancak büyük Python hayranları olmayanlar, AWKtemelde aynı şeyi yapmak için kullanabiliriz : diziyi tersine çevir ve yazdır:

 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
ya da biraz daha temizpython3 -c 'import sys;print(*reversed(sys.argv[1:]))' $VAR
iruvar

@iruvar, temiz değil , kabuğun split + glob operatörünü $IFSdoğru karakterleri içerdiğinden emin olmadan ve glob bölümünü devre dışı bırakmadan çağırır .
Stéphane Chazelas

@ StéphaneChazelas, yeterince doğru - python kısmı yine de daha temiz (IMHO). Yani, hiç geri gidiş "$VAR"verimpython3 -c 'import sys;print(*reversed(sys.argv[1].split()))' "$VAR"
Iruvar

1

zsh:

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

$=VARböler $VARüzerinde $IFS. (Oa)sonuç listesini ters dizi sırasıyla sıralar. print -r --(gibi ksh), echo -E -( zshspesifik) ile aynı güvenilir sürümdürecho : bağımsız değişkenlerini boşlukla ayrılmış, satırsonu ile sonlandırılır.

Yalnızca uzayda bölmek istiyorsanız ve $IFS(boşluk, sekme, yeni satır, varsayılan olarak nul) içeren öğelere değil, ya alan atayın ya $IFSda aşağıdaki gibi açık bir bölme kullanın:

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

Ters sayısal sırada sıralamak için:

$ 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 (böylece de çalışır bash):

Kabuk yerleşik (yalnızca printfbazı kabuklar hariç ) mekanizmalarıyla (kısa değerli değişkenler için daha iyi):

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"

İle awk(büyük değişkenler için daha iyi, özellikle bash, ancak bağımsız değişkenlerin (veya tek bir bağımsız değişkenin) boyutunun sınırına kadar):

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

0

Yetersiz POSIX yazılım araçları tek katmanlı :

echo `echo $VAR | tr ' ' '\n' | sort -nr`
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.