BASH - 随机排列多行字符串中的字符
BASH - Shuffle characters in strings from several rows
我有一个具有以下结构的文件 (filename.txt):
>line1
ABC
DEF
GHI
>line2
JKL
MNO
PQR
>line3
STU
VWX
YZ
我想打乱不以>
开头的字符串中的字符。输出(例如)如下所示:
>line1
DGC
FEI
HBA
>line2
JRP
OKN
QML
>line3
SZV
YXT
UW
这就是我尝试为每个 >line[number]
: ruby -lpe '$_ = $_.chars.shuffle * "" if !/^>/' filename.txt
打乱字符的方法。该命令有效(请参阅我的 post )但它会逐行随机播放。我想知道如何修改命令以在每个 >line[number]
的 all 字符串之间随机排列字符)。使用 ruby
不是必需的。
#!/bin/bash
# echo > output.txt # uncomment to write in a file output.txt
mix(){
{
echo "$title"
line="$( fold -w1 <<< "$line" | shuf )"
echo "${line//$'\n'}" | fold -w3
} # >> output.txt # uncomment to write in a file output.txt
unset line
}
while read -r; do
if [[ $REPLY =~ ^\> ]]; then
mix
title="$REPLY"
else
line+="$REPLY"
fi
done < filename.txt
mix # final mix after loop's exit, otherwise line3 will be not mixed
exit
的评论
首先我们要解决的问题是:如何将多行中的所有字符打乱:
echo -e 'ABC\nDEF\nGHI' |grep -o . |shuf |tr -d '\n'
GDAFHEIBC
而且,我们还需要一个数组来记录原始字符串中每一行的长度。
s=GDAFHEIBC
lens=(3 3 3)
start=0
for len in "${lens[@]}"; do
echo ${s:${start}:${len}}
((start+=len))
done
GDA
FHE
IBC
所以,原点多行:
ABC
DEF
GHI
已改组为:
GDA
FHE
IBC
现在,我们可以做我们的工作了:
lens=()
string=""
function shuffle_lines {
local start=0
local shuffled_string=$(grep -o . <<< ${string} |shuf |tr -d '\n')
for len in "${lens[@]}"; do
echo ${shuffled_string:${start}:${len}}
((start+=len))
done
lens=()
string=""
}
while read -r line; do
if [[ "${line}" =~ ^\> ]]; then
shuffle_lines
echo "${line}"
else
string+="${line}"
lens+=(${#line})
fi
done <filename.txt
shuffle_lines
示例:
$ cat filename.txt
>line1
ABC
DEF
GHI
>line2
JKL
MNO
PQR
>line3
STU
VWX
YZ
>line4
0123
456
78
9
$ ./solution.sh
>line1
HFG
BED
AIC
>line2
JOP
KMQ
RLN
>line3
UVW
TYZ
XS
>line4
1963
245
08
7
首先创建一个测试文件。
str =<<FINI
>line1
ABC
DEF
GHI
>line2
JKL
MNO
PQR
>line3
STU
VWX
YZ
FINI
File.write('test', str)
#=> 56
现在读取文件并执行所需的操作。
result = File.read('test').split(/(>line\d+)/).map do |s|
if s.match?(/\A(?:|>line\d+)\z/)
s
else
a = s.scan(/\p{Lu}/).shuffle
s.gsub(/\p{Lu}/) { a.shift }
end
end.join
# ">line1\nECF\nHIA\nGBD\n>line2\nJNP\nKLR\nOQM\n>line3\nTXY\nUZV\nSW\n"
puts result
>line1
ECF
HIA
GBD
>line2
JNP
KLR
OQM
>line3
TXY
UZV
SW
要从命令执行此操作,请将代码转换为字符串,语句之间用分号分隔。
ruby -e "puts (File.read('test').split(/(>line\d+)/).map do |s|; if s.match?(/\A(?:|>line\d+)\z/); s; else; a = s.scan(/\p{Lu}/).shuffle; s.gsub(/\p{Lu}/) { a.shift }; end; end).join"
步骤如下
a = File.read('test')
#=> ">line1\nABC\nDEF\nGHI\n>line2\nJKL\nMNO\nPQR\n>line3\nSTU\nVWX\nYZ\n"
b = a.split(/(>line\d+)/)
#=> ["", ">line1", "\nABC\nDEF\nGHI\n", ">line2", "\nJKL\nMNO\nPQR\n",
# ">line3", "\nSTU\nVWX\nYZ\n"]
请注意,作为 split
参数的正则表达式将 >line\d+
置于捕获组中。否则,">line1"
、">line2"
和 ">line3"
将不会包含在 b
.
中
c = b.map do |s|
if s.match?(/\A(?:|>line\d+)\z/)
s
else
a = s.scan(/\p{Lu}/).shuffle
s.gsub(/\p{Lu}/) { a.shift }
end
end
#=> ["", ">line1", "\nEAC\nIHB\nDGF\n", ">line2", "\nKQJ\nROL\nMPN\n",
# ">line3", "\nSUY\nXTV\nZW\n"]
c.join
#=> ">line1\nEAC\nIHB\nDGF\n>line2\nKQJ\nROL\nMPN\n>line3\nSUY\nXTV\nZW\n"
现在更仔细地考虑 c
的计算。 b
的第一个元素被传递给块,块变量 s
被设置为其值:
s = ""
然后我们计算
s.match?(/\A(?:|>line\d+)\z/)
#=> true
所以 ""
是从块中 return 编辑的。正则表达式可以表示如下。
/
\A # match the beginning of the string
(?: # begin a non-capture group
# match an empty space
| # or
>line\d+ # match '>line' followed by one or more digits
) # end non-capture group
\z # match the end of the string
/x # free-spacing regex definition mode.
在 non-capture 组中匹配了一个空的 space。
然后将 b
的下一个元素传递给块。
s = ">line1"
再次
s.match?(/\A(?:|>line\d+)\z/)
#=> true
所以 s
是来自区块的 return。
现在 b
的第三个元素被传递给块。 (最后,有趣的事情。)
s = "\nABC\nDEF\nGHI\n"
d = s.scan(/\p{Lu}/)
#=> ["A", "B", "C", "D", "E", "F", "G", "H", "I"]
a = d.shuffle
#=> ["D", "C", "G", "H", "B", "F", "I", "E", "A"]
s.gsub(/\p{Lu}/) { a.shift }
#=> "\nDCG\nHBF\nIEA\n"
其余计算类似。
我有一个具有以下结构的文件 (filename.txt):
>line1
ABC
DEF
GHI
>line2
JKL
MNO
PQR
>line3
STU
VWX
YZ
我想打乱不以>
开头的字符串中的字符。输出(例如)如下所示:
>line1
DGC
FEI
HBA
>line2
JRP
OKN
QML
>line3
SZV
YXT
UW
这就是我尝试为每个 >line[number]
: ruby -lpe '$_ = $_.chars.shuffle * "" if !/^>/' filename.txt
打乱字符的方法。该命令有效(请参阅我的 post >line[number]
的 all 字符串之间随机排列字符)。使用 ruby
不是必需的。
#!/bin/bash
# echo > output.txt # uncomment to write in a file output.txt
mix(){
{
echo "$title"
line="$( fold -w1 <<< "$line" | shuf )"
echo "${line//$'\n'}" | fold -w3
} # >> output.txt # uncomment to write in a file output.txt
unset line
}
while read -r; do
if [[ $REPLY =~ ^\> ]]; then
mix
title="$REPLY"
else
line+="$REPLY"
fi
done < filename.txt
mix # final mix after loop's exit, otherwise line3 will be not mixed
exit
的评论
首先我们要解决的问题是:如何将多行中的所有字符打乱:
echo -e 'ABC\nDEF\nGHI' |grep -o . |shuf |tr -d '\n'
GDAFHEIBC
而且,我们还需要一个数组来记录原始字符串中每一行的长度。
s=GDAFHEIBC
lens=(3 3 3)
start=0
for len in "${lens[@]}"; do
echo ${s:${start}:${len}}
((start+=len))
done
GDA
FHE
IBC
所以,原点多行:
ABC
DEF
GHI
已改组为:
GDA
FHE
IBC
现在,我们可以做我们的工作了:
lens=()
string=""
function shuffle_lines {
local start=0
local shuffled_string=$(grep -o . <<< ${string} |shuf |tr -d '\n')
for len in "${lens[@]}"; do
echo ${shuffled_string:${start}:${len}}
((start+=len))
done
lens=()
string=""
}
while read -r line; do
if [[ "${line}" =~ ^\> ]]; then
shuffle_lines
echo "${line}"
else
string+="${line}"
lens+=(${#line})
fi
done <filename.txt
shuffle_lines
示例:
$ cat filename.txt
>line1
ABC
DEF
GHI
>line2
JKL
MNO
PQR
>line3
STU
VWX
YZ
>line4
0123
456
78
9
$ ./solution.sh
>line1
HFG
BED
AIC
>line2
JOP
KMQ
RLN
>line3
UVW
TYZ
XS
>line4
1963
245
08
7
首先创建一个测试文件。
str =<<FINI
>line1
ABC
DEF
GHI
>line2
JKL
MNO
PQR
>line3
STU
VWX
YZ
FINI
File.write('test', str)
#=> 56
现在读取文件并执行所需的操作。
result = File.read('test').split(/(>line\d+)/).map do |s|
if s.match?(/\A(?:|>line\d+)\z/)
s
else
a = s.scan(/\p{Lu}/).shuffle
s.gsub(/\p{Lu}/) { a.shift }
end
end.join
# ">line1\nECF\nHIA\nGBD\n>line2\nJNP\nKLR\nOQM\n>line3\nTXY\nUZV\nSW\n"
puts result
>line1
ECF
HIA
GBD
>line2
JNP
KLR
OQM
>line3
TXY
UZV
SW
要从命令执行此操作,请将代码转换为字符串,语句之间用分号分隔。
ruby -e "puts (File.read('test').split(/(>line\d+)/).map do |s|; if s.match?(/\A(?:|>line\d+)\z/); s; else; a = s.scan(/\p{Lu}/).shuffle; s.gsub(/\p{Lu}/) { a.shift }; end; end).join"
步骤如下
a = File.read('test')
#=> ">line1\nABC\nDEF\nGHI\n>line2\nJKL\nMNO\nPQR\n>line3\nSTU\nVWX\nYZ\n"
b = a.split(/(>line\d+)/)
#=> ["", ">line1", "\nABC\nDEF\nGHI\n", ">line2", "\nJKL\nMNO\nPQR\n",
# ">line3", "\nSTU\nVWX\nYZ\n"]
请注意,作为 split
参数的正则表达式将 >line\d+
置于捕获组中。否则,">line1"
、">line2"
和 ">line3"
将不会包含在 b
.
c = b.map do |s|
if s.match?(/\A(?:|>line\d+)\z/)
s
else
a = s.scan(/\p{Lu}/).shuffle
s.gsub(/\p{Lu}/) { a.shift }
end
end
#=> ["", ">line1", "\nEAC\nIHB\nDGF\n", ">line2", "\nKQJ\nROL\nMPN\n",
# ">line3", "\nSUY\nXTV\nZW\n"]
c.join
#=> ">line1\nEAC\nIHB\nDGF\n>line2\nKQJ\nROL\nMPN\n>line3\nSUY\nXTV\nZW\n"
现在更仔细地考虑 c
的计算。 b
的第一个元素被传递给块,块变量 s
被设置为其值:
s = ""
然后我们计算
s.match?(/\A(?:|>line\d+)\z/)
#=> true
所以 ""
是从块中 return 编辑的。正则表达式可以表示如下。
/
\A # match the beginning of the string
(?: # begin a non-capture group
# match an empty space
| # or
>line\d+ # match '>line' followed by one or more digits
) # end non-capture group
\z # match the end of the string
/x # free-spacing regex definition mode.
在 non-capture 组中匹配了一个空的 space。
然后将 b
的下一个元素传递给块。
s = ">line1"
再次
s.match?(/\A(?:|>line\d+)\z/)
#=> true
所以 s
是来自区块的 return。
现在 b
的第三个元素被传递给块。 (最后,有趣的事情。)
s = "\nABC\nDEF\nGHI\n"
d = s.scan(/\p{Lu}/)
#=> ["A", "B", "C", "D", "E", "F", "G", "H", "I"]
a = d.shuffle
#=> ["D", "C", "G", "H", "B", "F", "I", "E", "A"]
s.gsub(/\p{Lu}/) { a.shift }
#=> "\nDCG\nHBF\nIEA\n"
其余计算类似。