通过单词反转变量的内容


13

所以如果我有一个变量

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

并回声

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

但是,在脚本的更下方,我需要反转此变量的顺序,以便显示为类似

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

我尝试使用rev,它实际上颠倒了一切,所以它作为

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

2
您可能会在以下类似问题的答案中找到一些有用的建议:如何向后读取IP地址?(只需要调整空间分隔符来代替句点)
steeldriver,

Answers:


21

在GNU系统上,cat的反面是tac:

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

干杯! 做到了。我还有很多东西要学习
Alistair Hardy

@JhonKugelman如果没有回声,输出将有一个附加的换行符,等效于此:echo $'100 \n90 80 70 60 50 40 30 20 10'
sorontar '16

在当前的tac -s" " <<< "$VAR "版本中,它仍然插入一个前导空白行,增加了尾部空间,省略了尾部的换行符(好像printf '\n%s ' "$reversed_VAR"而不是预期printf '%s\n' "$reversed_VAR"
斯特凡Chazelas

@don_crissti原始答案:echo $(tac -s" " <<< $VAR)没有尾随空格,因为$(…)如果IFS有尾随换行和空格,则removes将删除尾随换行和空格。谢谢,我的解决方案将很快得到纠正。
sorontar '16

6

对于简短列表和变量,shell本身是最快的解决方案:

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

输出:

100 90 80 70 60 50 40 30 20 10 

注:在拆分操作set -- $var是这里使用的确切字符串安全的,但不会是这样,如果字符串中包含通配符(*?[])。set -f如果需要,可以在拆分之前避免它。相同的注释也适用于以下解决方案(除非另有说明)。

或者,如果要使用反向列表设置其他变量:

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

或仅使用位置参数。

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"

或仅使用一个变量(本身可能是var):
注意:此解决方案不受gloglo或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 营救

$ 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


perl,礼貌如何反向读取IP地址?由@steeldriver共享

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


或者,bash通过将字符串转换为数组本身

$ 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

您可以使用bash功能(例如数组)完全在过程中进行操作:

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

如果$ VAR不是很大,这应该是最快的方法。


但是,这不会扩展到其值包含通配符(例如VAR='+ * ?')的变量。使用set -o noglobset -f修复该问题。更一般而言,$IFS使用split + glob运算符时,请考虑glob 的值和状态。不,那是毫无意义使用该运营商rev_a+=( ${a[$i]} )rev_a+=( "${a[$i]}" )会使得很多更有意义。
斯特凡Chazelas


1

在这里使用一些Python魔术:

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

它的工作方式非常简单:

  • 我们将其"$VAR"称为第二个命令行参数sys.argv[1](带有-coption的第一个参数始终就是该参数-c本身)。请注意在引号周围,"$VAR"以防止将变量本身拆分为单个单词(his由shell而不是python来完成)
  • 接下来,我们使用.split()方法,将其分成字符串列表
  • [::-1]部分只是扩展的Slice语法 以获得列表的反向
  • 最后,我们将所有内容重新组合成一个字符串" ".join()并打印出来

但是那些不是Python忠实粉丝的人,我们可以AWK用来做本质上相同的事情:拆分并反向打印数组:

 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
还是比较干净python3 -c 'import sys;print(*reversed(sys.argv[1:]))' $VAR
iruvar

@iruvar,不是更清洁的,它将调用shell的split + glob运算符,而不确保$IFS包含正确的字符并且不禁用glob部分。
斯特凡Chazelas

@StéphaneChazelas,足够正确-python部分仍然更干净(IMHO)。因此,回到"$VAR"收益python3 -c 'import sys;print(*reversed(sys.argv[1].split()))' "$VAR"
iruvar

1

zsh

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

$=VAR分割$VAR$IFS(Oa)以反向数组顺序排序结果列表。print -r --(如中的ksh),与echo -E -zsh特定)相同是的可靠版本echo:打印其参数,并按空格分隔,并以换行符终止。

如果您只想在空间上拆分,而不是在任何$IFS包含的内容上拆分(默认情况下是空格,制表符,换行符,nul),请为分配空间$IFS,或使用类似以下的显式拆分:

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

以相反的数字顺序排序:

$ 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(因此也可以使用bash):

仅使用内置的shell(printf某些shell中除外)机制(对于具有短值的变量更好):

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"

With awk(最好使用大变量,尤其是with bash,但要达到参数(或单个参数)大小的限制):

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

0

优雅的POSIX软件工具一线:

echo `echo $VAR | tr ' ' '\n' | sort -nr`
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.