Đảo ngược nội dung của một biến bằng lời nói


13

Vì vậy, nếu tôi có một biến

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

và lặp lại

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

Tuy nhiên, tiếp tục xuống đoạn script tôi cần đảo ngược thứ tự của biến này để nó hiển thị dưới dạng như

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

Tôi đã thử sử dụng rev và nó hoàn toàn đảo ngược mọi thứ để nó xuất hiện dưới dạng

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

2
Bạn có thể tìm thấy một số gợi ý hữu ích trong câu trả lời cho câu hỏi tương tự này: Làm thế nào để đọc ngược địa chỉ IP? (chỉ cần điều chỉnh cho các dải phân cách không gian thay cho các thời kỳ)
Steeldo

Câu trả lời:


21

Trên các hệ thống GNU, mặt trái của con mèo là tac:

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

Chúc mừng! Điều đó đã lừa Tôi vẫn còn nhiều điều phải học hỏi
Alistair Hardy

@JhonKugelman Không có tiếng vang, đầu ra có thêm một dòng mới, tương đương với điều này:echo $'100 \n90 80 70 60 50 40 30 20 10'
sorontar

Với tac -s" " <<< "$VAR "phiên bản hiện tại , nó vẫn chèn một dòng trống hàng đầu, thêm một khoảng trắng ở cuối và bỏ qua ký tự dòng mới (như thể printf '\n%s ' "$reversed_VAR"thay vì dự kiến printf '%s\n' "$reversed_VAR")
Stéphane Chazelas 4/11/2016

@don_crissti Câu trả lời ban đầu: echo $(tac -s" " <<< $VAR)không có $(…)khoảng trắng ở cuối vì loại bỏ các dòng mới và dấu cách nếu IFS có chúng. Giải pháp của tôi sẽ được sửa chữa sớm, cảm ơn.
sorontar

6

Đối với một danh sách ngắn và trong một biến, chính shell là giải pháp nhanh nhất:

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

Đầu ra:

100 90 80 70 60 50 40 30 20 10 

Lưu ý: thao tác phân tách trong set -- $varan toàn cho chuỗi chính xác được sử dụng ở đây, nhưng sẽ không như vậy nếu chuỗi chứa ký tự đại diện ( *, ?hoặc []). Nó có thể được tránh với set -ftrước khi chia, nếu cần. Lưu ý tương tự cũng có giá trị cho các giải pháp sau (trừ khi có ghi chú).

Hoặc, nếu bạn muốn đặt một số biến khác với danh sách ngược lại:

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

Hoặc chỉ sử dụng các tham số vị trí.

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"

Hoặc chỉ sử dụng một biến (có thể là var):
Lưu ý: Giải pháp này không bị ảnh hưởng bởi sự hả hê cũng như 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 để giải cứu

$ 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


với perl, lịch sự Làm thế nào để đọc một địa chỉ IP ngược? được chia sẻ bởi @steel ấn

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


Hoặc, với bashchính nó bằng cách chuyển đổi chuỗi thành mảng

$ 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

Bạn có thể sử dụng các tính năng bash như mảng để thực hiện hoàn toàn trong quá trình:

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

Đây phải là cách nhanh nhất để làm điều đó với điều kiện $ VAR không quá lớn.


Tuy nhiên, điều đó không mở rộng đến các biến có giá trị chứa các ký tự đại diện (như VAR='+ * ?'). Sử dụng set -o noglobhoặc set -fđể khắc phục điều đó. Tổng quát hơn, nên xem xét giá trị $IFSvà trạng thái toàn cầu khi sử dụng toán tử split + global. Không có ý nghĩa gì khi sử dụng toán tử đó rev_a+=( ${a[$i]} ), rev_a+=( "${a[$i]}" )sẽ có ý nghĩa hơn nhiều.
Stéphane Chazelas

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

1

Sử dụng một chút phép thuật Python tại đây:

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

Cách thức hoạt động này khá đơn giản:

  • chúng tôi gọi "$VAR"sys.argv[1]tham số dòng lệnh thứ hai (tham số đầu tiên với -ctùy chọn luôn luôn là như vậy - -cchính nó. Lưu ý trích dẫn xung quanh "$VAR"để ngăn phân tách biến thành các từ riêng lẻ (mà anh ấy thực hiện bằng shell, không phải python)
  • Tiếp theo chúng ta sử dụng .split()phương thức, để chia nó thành một danh sách các chuỗi
  • [::-1]một phần chỉ là cú pháp lát mở rộng để đảo ngược danh sách
  • cuối cùng chúng tôi kết hợp mọi thứ lại với nhau thành một chuỗi " ".join()và in ra

Nhưng những người không phải là người hâm mộ Python lớn, chúng ta có thể sử dụng AWKđể thực hiện cùng một việc: tách và in mảng ngược lại:

 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
hoặc có phần sạch hơnpython3 -c 'import sys;print(*reversed(sys.argv[1:]))' $VAR
iruvar 4/11/2016

@iruvar, không sạch hơn , sẽ gọi toán tử chia + toàn cầu của shell mà không đảm bảo $IFSchứa các ký tự phù hợp và không vô hiệu hóa phần toàn cầu.
Stéphane Chazelas

@ StéphaneChazelas, đủ đúng - phần trăn vẫn sạch hơn (IMHO). Vì vậy, quay trở lại "$VAR"sản lượngpython3 -c 'import sys;print(*reversed(sys.argv[1].split()))' "$VAR"
iruvar

1

zsh:

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

$=VARchia tách $VARtrên $IFS. (Oa)sắp xếp danh sách kết quả theo thứ tự mảng ngược. print -r --(như trong ksh), giống như echo -E -( zshcụ thể) là một phiên bản đáng tin cậy củaecho : in các đối số của nó dưới dạng được phân tách bằng dấu cách, kết thúc bằng dòng mới.

Nếu bạn chỉ muốn phân tách trên không gian chứ không phải trên bất kỳ thứ gì $IFScó chứa (khoảng trắng, tab, dòng mới, nul theo mặc định), hoặc gán không gian cho $IFShoặc sử dụng phân tách rõ ràng như:

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

Để sắp xếp theo thứ tự số ngược:

$ 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 (cũng sẽ làm việc với bash ):

Với các printfcơ chế dựng sẵn shell (ngoại trừ trong một số shell) chỉ (tốt hơn cho các biến có giá trị ngắn):

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"

Với awk(tốt hơn cho các biến lớn, đặc biệt là với bash, nhưng đến giới hạn kích thước của các đối số (hoặc của một đối số)):

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

0

Thanh nha POSIX công cụ phần mềm một lót:

echo `echo $VAR | tr ' ' '\n' | sort -nr`
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.