Membalik urutan elemen pembatas titik dalam string

14

Saya memiliki string input seperti:

arg1.arg2.arg3.arg4.arg5

Output yang saya inginkan adalah:

arg5.arg4.arg3.arg2.arg1

Tidak selalu 5 arg, bisa 2 hingga 10.

Bagaimana saya bisa melakukan ini dalam skrip bash?

Jens
sumber

Jawaban:

22
  • Menggunakan kombinasi tr+ tac+paste

    $ tr '.' $'\n' <<< 'arg1.arg2.arg3.arg4.arg5' | tac | paste -s -d '.'
    arg5.arg4.arg3.arg2.arg1
  • Jika Anda masih lebih suka bash, Anda bisa melakukannya dengan cara ini,

    IFS=. read -ra line <<< "arg1.arg2.arg3.arg4."
    let x=${#line[@]}-1; 
    while [ "$x" -ge 0 ]; do 
          echo -n "${line[$x]}."; 
          let x--; 
    done
  • Menggunakan perl,

    $ echo 'arg1.arg2.arg3.arg4.arg5' | perl -lne 'print join ".", reverse split/\./;'
    arg5.arg4.arg3.arg2.arg1
Rahul
sumber
2
Bah, saya hanya akan memposting solusi Perl :)
ilkkachu
10

Jika Anda tidak keberatan sedikit python:

".".join(s.split(".")[::-1])

Contoh:

$ python3 -c 's="arg1.arg2.arg3.arg4.arg5"; print(".".join(s.split(".")[::-1]))'
arg5.arg4.arg3.arg2.arg1
  • s.split(".")membuat daftar yang berisi .substring yang terpisah, [::-1]membalik daftar dan ".".join()bergabung dengan komponen dari daftar terbalik ..
heemayl
sumber
5

Anda dapat melakukannya dengan satu sedpermintaan:

sed -E 'H;g;:t;s/(.*)(\n)(.*)(\.)(.*)/\1\4\5\2\3/;tt;s/\.(.*)\n/\1./'

ini menggunakan buffer penahan untuk mendapatkan baris baru di ruang pola dan menggunakannya untuk melakukan permutasi sampai baris baru tidak lagi diikuti oleh titik mana titik itu menghapus titik terkemuka dari ruang pola dan menggantikan baris baru dengan titik.
Dengan BRE dan regex yang sedikit berbeda:

sed 'H
g
:t
s/\(.*\)\(\n\)\(.*\)\(\.\)\(.*\)/\1\4\5\2\3/
tt
s/\(\.\)\(.*\)\n/\2\1/'

Jika input terdiri dari lebih dari satu baris dan Anda ingin membalik urutan pada setiap baris:

sed -E 'G;:t;s/(.*)(\.)(.*)(\n)(.*)/\1\4\5\2\3/;tt;s/(.*)\n(\.)(.*)/\3\2\1/'
don_crissti
sumber
3

Solusi SED:

sed 's/\./\n/g' yourFile | tac | sed ':a; $!{N;ba};s/\n/./g'
FarazX
sumber
3

Dengan zsh:

$ string=arg1.arg2.arg3.arg4.arg5
$ printf '%s\n' ${(j:.:)${(Oas:.:)string}}
arg5.arg4.arg3.arg2.arg1

Untuk mempertahankan elemen kosong:

$ string=arg1.arg2.arg3.arg4..arg5.
$ printf '%s\n' ${(j:.:)"${(@Oas:.:)string}"}
.arg5..arg4.arg3.arg2.arg1
  • s:.:: terbelah .
  • Oa: sort array secara terbalik sebuah indray rray o rder
  • j:.:: bergabunglah ..
  • @: lakukan "$@"ekspansi-like ketika dalam tanda kutip ganda sehingga ekspansi tidak mengalami penghapusan-kosong.

bashSetara POSIX (atau ) dapat berupa sesuatu seperti:

string=arg1.arg2.arg3.arg4..arg5.

IFS=.; set -f
set -- $string$IFS # split string into "$@" array
unset pass
for i do # reverse "$@" array
  set -- "$i" ${pass+"$@"}
  pass=1
done
printf '%s\n' "$*" # "$*" to join the array elements with the first
                   # character of $IFS

(perhatikan bahwa zsh(dalam shemulasi), yashdan beberapa pdkshcangkang berbasis bukan POSIX dalam hal ini karena mereka memperlakukan $IFSsebagai pemisah medan alih-alih pembatas lapangan)

Di mana ia berbeda dari beberapa solusi lain yang diberikan di sini adalah bahwa ia tidak memperlakukan karakter baris baru (dan dalam kasus zsh, yang NUL baik) khususnya, yang $'ab.cd\nef.gh'akan dibalik ke $'gh.cd\nef.ab', bukan $'cd.ab\ngh.ef'atau $'gh.ef.cd.ab'.

Stéphane Chazelas
sumber
Ada apa denganmu IFS="."; set -f; set -- $string$IFS; for i; do rev="$i$IFS$rev"; done; printf '%s\n' "${rev%$IFS}"?
@BinaryZebra Nothing (selain mungkin dari for i; doyang bukan POSIX (belum) bahkan jika itu didukung oleh semua kerang POSIX yang saya tahu). Hanya saja solusi itu adalah terjemahan langsung dari solusi tersebut zsh(dipecah menjadi array, array terbalik, bergabung dengan elemen array) tetapi menggunakan operator POSIX-only. (Saya telah mengubah string=$string$IFS; set -- $stringke set -- $string$IFSyang tampaknya bekerja secara konsisten di shell, terima kasih).
Stéphane Chazelas
2

Saya melihat Rahul sudah memposting solusi bash asli (antara lain), yang merupakan versi lebih pendek dari yang pertama saya buat:

function reversedots1() (
  IFS=. read -a array <<<"$1"
  new=${array[-1]}
  for((i=${#array[*]} - 2; i >= 0; i--))
  do
    new=${new}.${array[i]}
  done
  printf '%s\n' "$new"
)

tapi saya tidak bisa berhenti di situ, dan merasa perlu untuk menulis solusi bash-centric rekursif:

function reversedots2() {
  if [[ $1 =~ ^([^.]*)\.(.*)$ ]]
  then
    printf %s $(reversedots2 "${BASH_REMATCH[2]}") . "${BASH_REMATCH[1]}"
  else
    printf %s "$1"
  fi
}
Jeff Schaller
sumber
2

Tidak melihat awkjawaban, jadi ini satu:

 echo 'arg1.arg2.arg3' | awk -F. '{for (i=NF; i>0; --i) printf "%s%s", (i<NF ? "." : ""), $i; printf "\n"}'
Paul Evans
sumber
1

Pure Bash, membutuhkan periode tambahan pada input:

echo 'foo.bar.baz.' | ( while read -d . f;do g="$f${g+.}$g" ;done;echo "$g" )

Gunakan printf %s "$g"jika salah satu elemen bertitik bisa mulai dengan tanda hubung.

Eric
sumber