【问题标题】:How to reverse a list of words in a shell string?如何反转shell字符串中的单词列表?
【发布时间】:2015-02-26 12:27:45
【问题描述】:

我有一个字符串中的单词列表:

str="SaaaaE SeeeeE SbbbbE SffffE SccccE"

我想反转它以得到

"SccccE SffffE SbbbbE SeeeeE SaaaaE"

我如何使用ash 做到这一点?

【问题讨论】:

    标签: linux shell awk sed ash


    【解决方案1】:

    你可以使用awk如下:

    echo "$str" | awk '{ for (i=NF; i>1; i--) printf("%s ",$i); print $1; }'
    

    【讨论】:

      【解决方案2】:

      是的,你可以试试这些命令,

      对于字符串

      echo "aaaa eeee bbbb ffff cccc"|tr ' ' '\n'|tac|tr '\n' ' '
      

      对于变量

      echo $str|tr ' ' '\n'|tac|tr '\n' ' '
      

      【讨论】:

      • 我的 ash 不支持 tac
      • 好的,对不起,我在 bash 中运行了这个命令,但我保留了这个命令以供将来的读者帮助。
      【解决方案3】:

      你可以使用 awk:

      echo "aaaa eeee bbbb ffff cccc" | awk '{for(i=NF;i>0;--i)printf "%s%s",$i,(i>1?OFS:ORS)}'
      

      在字段中向后循环,打印每个字段。 OFS 是输出字段分隔符(默认为空格),ORS 是输出记录分隔符(换行符)。

      我假设您不希望每个单词中的字母顺序颠倒。

      【讨论】:

        【解决方案4】:

        如果您需要纯 shell,无需外部工具,请考虑:

        reverse_word_order() {
            local result=
            for word in $@; do
                result="$word $result"
            done
            echo "$result" 
        }
        
        reverse_word_order "$str"
        

        否则tac可以直接帮助你:

        echo -n "$str" | tac -s' '
        

        tac -s' ' <<<"$str" | xargs 
        

        【讨论】:

          【解决方案5】:

          这是一个使用 do whileawk(在 Stackoverflow 上使用不多)
          不需要额外的变量i

          echo "aaaa eeee bbbb ffff cccc"|awk '{do printf "%s"(NF>1?FS:RS),$NF;while(--NF)}'
          cccc ffff bbbb eeee aaaa
          

          【讨论】:

          • 略短awk '{do printf "%s"(NF&gt;1?OFS:ORS),$NF;while (--NF)}'
          • @Jidder,谢谢。使用 FS:RS 而不是 OFS:ORS 会更短
          • --NF 的效果是每个 POSIX 的未定义行为,因此在不同的 awk 版本中其行为会有所不同。
          【解决方案6】:

          这适用于sh(我没有ash,所以无法验证):

          reverse() {
              for (( i = ${#*}; i > 0; i-- ))
              {
                  echo ${!i}
              }
          }
          

          像这样使用它:

          $ reversed=$(reverse foo bar baz)
          $ echo $reversed
          baz bar foo
          

          这里有一些替代方案。一、不使用C风格的for

          reverse() {
              while [ ${#*} -gt 0 ]
              do
                echo ${*: -1}
                set -- "${@:1:$(($#-1))}"
              done
          }
          

          下一个(来自here)适用于替换不适用的地方(例如在 Android 上)

          reverse() {
              if [ "$#" -gt 0 ]; then
                  local arg=$1
                  shift
                  reverse "$@"
                  printf '%s\n' "$arg"
              fi
          }
          

          我试图找到一个适用于 Android 脚本的解决方案,其中 sh 不喜欢 C 风格的 for 或复杂的替换操作。我最终选择了最后一个示例,因为它没有使用任何内容。

          【讨论】:

          • 哪个高尔夫球适合 rwo(){ for((i=${#*};i>0;i--)){ echo ${!i};};}
          【解决方案7】:

          如果使用 Haskell 是可以接受的,有一个很好的工具叫做 hawk (haskell-awk) 可以很容易地做到这一点。首先你需要安装它:

          $ cabal install haskell-awk
          

          然后:

          $ echo $str | hawk -m reverse
          

          您需要在您的PATH 中找到~/.cabal/bin 才能找到hawk

          【讨论】:

            【解决方案8】:

            sed 分组:

            str="SaaaaE SeeeeE SbbbbE SffffE SccccE"
            
            echo $str | sed 's/\(.* \)\(.* \)\(.* \)\(.* \)\(.*\)/\5 \4\3\2\1/g'
            

            【讨论】:

              【解决方案9】:

              使用do...while的awk解决方案在输入字符串为空时会抛出错误,而一些使用for循环的awk解决方案会在输入字符串为空时打印一个空行。这比以前的 awk 解决方案略短,没有--NF 在 POSIX 中具有未定义行为的麻烦,并且当输入为空字符串时它不会抛出错误或打印空行:

              $ echo "aaaa eeee bbbb ffff cccc" | awk '{i=NF;while(i)printf "%s",$i (i-->1?FS:RS)}'
              
              cccc ffff bbbb eeee aaaa
              

              【讨论】:

                【解决方案10】:
                /* Shell script to reverse the Input String */    
                echo " **** Program to Reverse a String **** "
                read -p " Enter Here : " text
                
                echo "You have entered : " $text
                
                echo -n "Reverse of String : "
                
                arr=($text)
                
                arrlength=${#arr[@]}
                
                arrlength=`expr $arrlength - 1`
                
                while [ $arrlength -ge 0 ]
                do
                
                echo -n ${arr[arrlength]}
                echo -n " "
                 arrlength=`expr $arrlength - 1`
                done
                
                echo
                

                输出


                **** 反转字符串的程序 ***


                在这里输入:我爱我的印度

                您已进入:我爱我的印度

                字符串的反转:印度我的爱我

                【讨论】:

                • awk 的答案更有启发性和实用性。使用 shell 读取行是禁忌的。更不用说提示用户了。这不是命令行是关于什么的。
                【解决方案11】:

                另一种纯 bash 解决方案:

                str='test a is this'; res=""; prev=""
                while [ "$prev" != "$str" ]; do res="$res${str##* } "; prev="$str"; str="${str% *}"; done
                

                【讨论】:

                  【解决方案12】:

                  如果要从文件中读取输入(此处询问:read line reverse from a file), 我创建了以下 bash 脚本,该脚本在不更改文件行顺序的情况下反向打印每行的单词(我需要这个,例如在读取单词 RTL 而不是说中文的 LTR 等时):

                  #!/bin/bash
                  
                  fileName="$1"
                  outputFile="ReversedFile.npp"
                  
                  echo > ${outputFile}
                  lineCount=`cat $fileName | wc -l`
                  for ((lineNum=1; ${lineNum} <= ${lineCount}; lineNum++))
                  do
                      echo
                      echo "Processing Line ${lineNum} of ${lineCount} ..."
                  
                      lineOfFile="`cat $fileName | head -${lineNum} | tail -1`"
                      echo "${lineOfFile}"
                  
                      rm -f wordFile.txt
                      for eachWord in `echo "${lineOfFile}"`
                      do
                          echo "${eachWord}" >> wordFile.txt
                      done
                  
                      if [ -e wordFile.txt ]; then
                          thisLine=""
                          wordCount=`cat wordFile.txt| wc -l`
                          for ((wordNum=${wordCount}; ${wordNum}>=1; wordNum--))
                          do
                              wordOfLine="`cat wordFile.txt | head -${wordNum} | tail -1`"
                              echo "${wordNum} of ${wordCount} is ${wordOfLine}"
                              thisLine+="${wordOfLine} "
                          done
                          echo "output"
                          echo "${thisLine}"
                          echo "${thisLine}" >> ${outputFile}
                          rm -f wordFile.txt
                      fi
                  done
                  
                  echo
                  echo "Output in File ${outputFile}"
                  


                  笔记:
                  1) 确保输入文件为 UNIX EOL 格式,否则每行的最后一个单词可能会被截断
                  2) 为简单起见,可能省略了一些错误检查

                  【讨论】:

                    【解决方案13】:

                    这是对 GNU sed 手册中 Reverse Characters of Lines 脚本的轻微改编:

                    #!/usr/bin/sed -f
                    
                    # Skip lines without blanks
                    /[[:blank:]]/! b
                    
                    # Embed line between newlines
                    s/^.*$/\
                    &\
                    /
                    
                    # Reset the t flag
                    tx
                    
                    # Label to jump to
                    :x
                    
                    # Swap first and last word between newlines for three or more words
                    s/\n\([^[:blank:]][^[:blank:]]*\) \(.*\) \([^[:blank:]][^[:blank:]]*\)\n/\3 \
                    \2\
                     \1/
                    
                    # Jump to label if there was a change on the line
                    tx
                    
                    # Swap first and last word between newlines for exactly two words
                    s/\n\([^[:blank:]][^[:blank:]]*\) \([^[:blank:]][^[:blank:]]*\)\n/\2\
                     \
                    \1/
                    
                    # Remove the newline markers
                    s/\n//g
                    

                    它做了一些假设,例如没有前导或尾随空格,并且所有单词都被一个空格分隔。带有前导或尾随空格的行保持不变,并且单词未全部由一个空格分隔的行将多余的空格留在原处,这可能不是我们想要的。

                    该脚本应与任何符合 POSIX 的 sed 和基本正则表达式 (BRE) 一起使用。使用 GNU sed 和扩展正则表达式 (ERE) 可以提高可读性,例如

                    s/\n(\S+) (.*) (\S+)\n/\3 \n\2\n \1/
                    

                    对于主要替换,但如果您首先使用 sed 来解决这个问题,那么可读性可能不是最重要的问题。

                    【讨论】:

                      【解决方案14】:

                      重击

                      这是我使用的函数,(未在 ash 中测试过)

                      #reverse_word_order
                      function rwo(){ tr ' ' '\n'<<<"$@"|tac|tr '\n' ' ';}
                      echo $(rwo 'Hello There I am You')
                      #based on https://stackoverflow.com/a/27703859/1153645
                      

                      短跑

                      (也适用于 bash,但有些人似乎厌恶 echo)

                      rwo(){ echo "$*"|tr ' ' '\n'|tac|tr '\n' ' ';}
                      

                      正如 0andriy 所暗示的,我们可以打这个,(或者如果你的平台有 tac 但没有 tr):

                      rwo(){ echo "$*"|tac -s " ";} #dash compatible 
                      rwo(){ tac -s " "<<<"$@";} #bashism
                      

                      【讨论】:

                      • tr没用tac -s “ “ ...
                      【解决方案15】:
                      echo "aaaa eeee bbbb ffff cccc" | xargs echo | xargs -n1 echo | tac | xargs echo 
                      

                      我几乎在所有事情上都使用 xargs。它可以将每个单词与-n1 放在一行中,但它可以将所有单词放回一行而不使用它

                      • 通过回显字符串取消引用它,以便下一个 xargs 将每个单词视为一个单词
                      • 将每个单词回显到自己的行中
                      • 颠倒行的顺序
                      • 再次将它们全部打印在一行上

                      tac 可能有一些参数可以替代其中一些步骤

                      【讨论】:

                        【解决方案16】:

                        对于那些想要一个不复杂的简单单行解决方案的人来说,tac 是不二之选。这是我所知道的使用taccommand substitution 的最简单方法:

                        echo $(tac -s ' ' <<< $str)
                        

                        【讨论】:

                          猜你喜欢
                          • 2015-12-13
                          • 1970-01-01
                          • 2013-04-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2013-11-13
                          • 2017-06-15
                          相关资源
                          最近更新 更多