【发布时间】:2015-02-26 12:27:45
【问题描述】:
我有一个字符串中的单词列表:
str="SaaaaE SeeeeE SbbbbE SffffE SccccE"
我想反转它以得到
"SccccE SffffE SbbbbE SeeeeE SaaaaE"
我如何使用ash 做到这一点?
【问题讨论】:
我有一个字符串中的单词列表:
str="SaaaaE SeeeeE SbbbbE SffffE SccccE"
我想反转它以得到
"SccccE SffffE SbbbbE SeeeeE SaaaaE"
我如何使用ash 做到这一点?
【问题讨论】:
你可以使用awk如下:
echo "$str" | awk '{ for (i=NF; i>1; i--) printf("%s ",$i); print $1; }'
【讨论】:
是的,你可以试试这些命令,
对于字符串,
echo "aaaa eeee bbbb ffff cccc"|tr ' ' '\n'|tac|tr '\n' ' '
对于变量,
echo $str|tr ' ' '\n'|tac|tr '\n' ' '
【讨论】:
你可以使用 awk:
echo "aaaa eeee bbbb ffff cccc" | awk '{for(i=NF;i>0;--i)printf "%s%s",$i,(i>1?OFS:ORS)}'
在字段中向后循环,打印每个字段。 OFS 是输出字段分隔符(默认为空格),ORS 是输出记录分隔符(换行符)。
我假设您不希望每个单词中的字母顺序颠倒。
【讨论】:
如果您需要纯 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
【讨论】:
这是一个使用 do while 的 awk(在 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>1?OFS:ORS),$NF;while (--NF)}'
FS:RS 而不是 OFS:ORS 会更短
--NF 的效果是每个 POSIX 的未定义行为,因此在不同的 awk 版本中其行为会有所不同。
这适用于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 或复杂的替换操作。我最终选择了最后一个示例,因为它没有使用任何内容。
【讨论】:
如果使用 Haskell 是可以接受的,有一个很好的工具叫做 hawk (haskell-awk) 可以很容易地做到这一点。首先你需要安装它:
$ cabal install haskell-awk
然后:
$ echo $str | hawk -m reverse
您需要在您的PATH 中找到~/.cabal/bin 才能找到hawk。
【讨论】:
sed 分组:
str="SaaaaE SeeeeE SbbbbE SffffE SccccE"
echo $str | sed 's/\(.* \)\(.* \)\(.* \)\(.* \)\(.*\)/\5 \4\3\2\1/g'
【讨论】:
使用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
【讨论】:
/* 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
输出
**** 反转字符串的程序 ***
在这里输入:我爱我的印度
您已进入:我爱我的印度
字符串的反转:印度我的爱我
【讨论】:
另一种纯 bash 解决方案:
str='test a is this'; res=""; prev=""
while [ "$prev" != "$str" ]; do res="$res${str##* } "; prev="$str"; str="${str% *}"; done
【讨论】:
如果要从文件中读取输入(此处询问: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) 为简单起见,可能省略了一些错误检查
【讨论】:
这是对 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 来解决这个问题,那么可读性可能不是最重要的问题。
【讨论】:
这是我使用的函数,(未在 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
【讨论】:
tac -s “ “ ...
echo "aaaa eeee bbbb ffff cccc" | xargs echo | xargs -n1 echo | tac | xargs echo
我几乎在所有事情上都使用 xargs。它可以将每个单词与-n1 放在一行中,但它可以将所有单词放回一行而不使用它
tac 可能有一些参数可以替代其中一些步骤
【讨论】:
对于那些想要一个不复杂的简单单行解决方案的人来说,tac 是不二之选。这是我所知道的使用tac 和command substitution 的最简单方法:
echo $(tac -s ' ' <<< $str)
【讨论】: