Thứ tự đảo ngược của các phần tử được phân cách bằng dấu chấm trong một chuỗi


14

Tôi có một chuỗi đầu vào như:

arg1.arg2.arg3.arg4.arg5

Đầu ra tôi muốn là:

arg5.arg4.arg3.arg2.arg1

Không phải lúc nào cũng là 5 arg, có thể là 2 đến 10.

Làm thế nào tôi có thể làm điều này trong một kịch bản bash?

Câu trả lời:


22
  • Sử dụng kết hợp tr+ tac+paste

    $ tr '.' $'\n' <<< 'arg1.arg2.arg3.arg4.arg5' | tac | paste -s -d '.'
    arg5.arg4.arg3.arg2.arg1
  • Nếu bạn vẫn thích bash, bạn có thể làm theo cách này,

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

    $ echo 'arg1.arg2.arg3.arg4.arg5' | perl -lne 'print join ".", reverse split/\./;'
    arg5.arg4.arg3.arg2.arg1

2
Bah, tôi chỉ định đăng giải pháp Perl :)
ilkkachu

10

Nếu bạn không phiền một chút python:

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

Thí dụ:

$ python3 -c 's="arg1.arg2.arg3.arg4.arg5"; print(".".join(s.split(".")[::-1]))'
arg5.arg4.arg3.arg2.arg1
  • s.split(".")tạo một danh sách chứa .các chuỗi con riêng biệt, [::-1]đảo ngược danh sách và ".".join()nối các thành phần của danh sách đảo ngược với ..

5

Bạn có thể làm điều đó với một sedlời mời duy nhất :

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

cái này sử dụng bộ đệm giữ để có được một dòng mới hàng đầu trong không gian mẫu và sử dụng nó để hoán vị cho đến khi dòng mới không còn theo sau bởi bất kỳ dấu chấm nào tại đó nó loại bỏ dấu chấm hàng đầu khỏi không gian mẫu và thay thế dòng mới bằng dấu chấm.
Với BRE và một regex hơi khác:

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

Nếu đầu vào bao gồm nhiều hơn một dòng và bạn muốn đảo ngược thứ tự trên mỗi dòng:

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

3

Giải pháp SED:

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

3

Với zsh:

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

Để bảo tồn các phần tử trống:

$ string=arg1.arg2.arg3.arg4..arg5.
$ printf '%s\n' ${(j:.:)"${(@Oas:.:)string}"}
.arg5..arg4.arg3.arg2.arg1
  • s:.:: tách ra .
  • Oa: sắp xếp mảng theo chiều ngược lại một chỉ số rray o rder
  • j:.:: Tham gia vào ..
  • @: "$@"mở rộng giống như khi ở dấu ngoặc kép để việc mở rộng không trải qua quá trình xóa trống.

bashTương đương POSIX (hoặc ) có thể là một cái gì đó như:

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

(lưu ý rằng zsh(trong shmô phỏng) yashvà một số pdkshvỏ dựa trên không phải là POSIX về mặt chúng được coi $IFSnhư một dấu tách trường thay vì dấu phân cách trường)

Trường hợp khác với một số giải pháp khác được đưa ra ở đây là nó không xử lý ký tự dòng mới (và trong trường hợp zsh, một trong số NUL), đặc biệt, nó $'ab.cd\nef.gh'sẽ bị đảo ngược $'gh.cd\nef.ab', không $'cd.ab\ngh.ef'hoặc $'gh.ef.cd.ab'.


Có chuyện gì với IFS="."; set -f; set -- $string$IFS; for i; do rev="$i$IFS$rev"; done; printf '%s\n' "${rev%$IFS}"vậy?

@BinaryZebra Không có gì (có thể ngoài for i; docái không phải là POSIX (chưa) ngay cả khi nó được hỗ trợ bởi tất cả các vỏ POSIX mà tôi biết). Đó chỉ là giải pháp đó là một bản dịch trực tiếp của zshmột (chia thành mảng, mảng ngược, nối các phần tử mảng) nhưng sử dụng các toán tử chỉ POSIX. (Tôi đã thay đổi string=$string$IFS; set -- $stringđến set -- $string$IFSnhư điều đó dường như làm việc nhất quán trên vỏ, nhờ).
Stéphane Chazelas

2

Tôi thấy Rahul đã đăng một giải pháp bash bản địa (trong số những giải pháp khác), đây là phiên bản ngắn hơn của giải pháp đầu tiên tôi nghĩ ra:

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"
)

nhưng tôi không thể dừng lại ở đó và cảm thấy cần phải viết một giải pháp bash-centric đệ quy:

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

2

Không thấy awkcâu trả lời, vì vậy đây là một câu trả lời:

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

1

Pure Bash, yêu cầu thời gian kéo dài trên đầu vào:

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

Sử dụng printf %s "$g"nếu bất kỳ phần tử chấm nào có thể bắt đầu bằng dấu gạch nối.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.