字符串中以点分隔的元素的相反顺序


14

我有一个输入字符串,如:

arg1.arg2.arg3.arg4.arg5

我想要的输出是:

arg5.arg4.arg3.arg2.arg1

并非总是5个arg,可能是2到10。

如何在bash脚本中执行此操作?

Answers:


22
  • 使用tr+ tac+的组合paste

    $ tr '.' $'\n' <<< 'arg1.arg2.arg3.arg4.arg5' | tac | paste -s -d '.'
    arg5.arg4.arg3.arg2.arg1
  • 如果您仍然喜欢bash,可以这样做

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

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

2
Bah,我只是要发布Perl解决方案:)
ilkkachu,2016年

10

如果您不介意python

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

例:

$ python3 -c 's="arg1.arg2.arg3.arg4.arg5"; print(".".join(s.split(".")[::-1]))'
arg5.arg4.arg3.arg2.arg1
  • s.split(".")生成包含.分隔的子字符串[::-1]的列表,将列表反向,并使用".".join()将反向列表的组件连接在一起.

5

您可以通过一次sed调用来做到这一点:

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

它使用保持缓冲区在模式空间中获取前导换行符,并使用它进行置换,直到换行符不再跟有任何点为止,此时该点将从模式空间中删除前导点,并将换行符替换为一个点。
使用BRE和略有不同的正则表达式:

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

如果输入包含多行,并且您想颠倒每一行的顺序:

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

3

SED解决方案:

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

3

zsh

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

要保留空元素:

$ string=arg1.arg2.arg3.arg4..arg5.
$ printf '%s\n' ${(j:.:)"${(@Oas:.:)string}"}
.arg5..arg4.arg3.arg2.arg1
  • s:.::拆分 .
  • Oa:排序阵列中的反向 rray指数之Ô刻申
  • j:.::加入.
  • @"$@"在双引号中执行类扩展,因此扩展不会进行空删除。

POSIX(或bash)等价物可能类似于:

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

(请注意,zsh(在sh仿真中)yash以及某些pdksh基于shell的组件在这方面不是POSIX$IFS视为字段分隔符而不是字段定界符)

它与此处给出的其他一些解决方案的不同之处在于,它不特别对待换行符(zsh对于NUL,则不对换行符进行处理),而是$'ab.cd\nef.gh'将其反转为$'gh.cd\nef.ab',而不是$'cd.ab\ngh.ef'$'gh.ef.cd.ab'


什么错IFS="."; set -f; set -- $string$IFS; for i; do rev="$i$IFS$rev"; done; printf '%s\n' "${rev%$IFS}"

@BinaryZebra什么也没有(也许除了我for i; do还不知道的所有POSIX shell支持的POSIX之外)。只是该解决方案是一种直接翻译zsh(拆分为数组,反向数组,连接数组元素),但使用仅POSIX运算符。(谢谢,我已经将更改为string=$string$IFS; set -- $stringset -- $string$IFS因为它似乎在各个shell上都能正常工作)。
斯特凡Chazelas

2

我看到Rahul已经发布了本机bash解决方案(以及其他解决方案),这是我提出的第一个bash解决方案的简短版本:

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

但我不能止步于此,感到有必要编写一个以bash为中心的递归解决方案:

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

2

没有看到awk答案,所以这是一个:

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

1

纯Bash,需要输入的尾随时间:

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

使用printf %s "$g"如有虚元素可以用一个连字符。

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.