我有一个字符串中的单词列表:
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
是输出记录分隔符(换行符)。
我假设您不希望每个单词中的字母顺序颠倒。
如果您需要纯外壳,无需外部工具,请考虑:
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
这是一个awk
using do
while
(在 Stackoverflow 上用的不多)
不需要额外的变量i
echo "aaaa eeee bbbb ffff cccc"|awk '{do printf "%s"(NF>1?FS:RS),$NF;while(--NF)}'
cccc ffff bbbb eeee aaaa
这适用于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
}
下一个(从这里开始)适用于替换不起作用的地方(例如在 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
你需要~/.cabal/bin
在你PATH
的找到hawk
。
sed 分组:
str="SaaaaE SeeeeE SbbbbE SffffE SccccE"
echo $str | sed 's/\(.* \)\(.* \)\(.* \)\(.* \)\(.*\)/\5 \4\3\2\1/g'
使用 awk 解决方案do...while
在输入字符串为空时会抛出错误,而一些使用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
输出
**** 反转字符串的程序 ***
在这里输入:我爱我的印度
您已进入:我爱我的印度
字符串的反转:印度我的爱我
这是我使用的功能,(未在灰中测试)
#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
这是对 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 来解决这个问题,那么可读性可能不是最关心的问题。
如果要从文件中读取输入(如此处询问:从文件中读取行反向),我创建了以下 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) 为简单起见,可能省略了一些错误检查
另一个纯 bash 解决方案:
str='test a is this'; res=""; prev=""
while [ "$prev" != "$str" ]; do res="$res${str##* } "; prev="$str"; str="${str% *}"; done
For those wanting a simple one-line solution that isn't convoluted, tac is the way to go. Here is the simplest way I know using tac
and command substitution:
echo $(tac -s ' ' <<< $str)
echo "aaaa eeee bbbb ffff cccc" | xargs echo | xargs -n1 echo | tac | xargs echo
我几乎所有东西都使用 xargs。它可以将每个单词放在一行中-n1
,但它可以将所有单词放回一行,没有它
tac
可能有可以替代其中一些步骤的参数
反转字符串,但将 endmark 保留在适当的位置。
#!/bin/bash
#set -x
read -p "Enter a sentence: " sentence
myArray=( $sentence )
counter="${#myArray[@]}"
i=0
for word in "${myArray[@]}"
do
if [ $counter -lt 1 ]
then break
fi
reverseArray[$i]="${myArray[$counter-1]}"
letterCounter=0
while read -n 1 letter
do
if [ "$letter" == '?' ] || [ "$letter" == '!' ] || [ "$letter" == '.' ]
then
endmark="$letter"
reverseArray[$i]=$(echo "${reverseArray[$i]}" | head -c "$letterCounter")
fi
((letterCounter++))
done < <(echo "${reverseArray[$i]}")
((i++))
((counter--))
done
echo "${reverseArray[*]}$endmark"
exit
输入:你好,世界?
输出:世界你好?