BASH - 随机排列文件中字符串中的字符

BASH - Shuffle characters in strings from file

我有一个具有以下结构的文件 (filename.txt):

>line1
ABC
>line2
DEF
>line3
GHI
>line4
JKL

我想打乱 而非 开头的字符串中的字符 >。输出将(例如)如下所示:

>line1
BCA
>line2
DFE
>line3
IHG
>line4
KLJ

这就是我尝试打乱字符串中字符的方法:sed 's/./&\n/' | shuf | tr -d "\n"。看起来它有效,但它没有考虑换行符。此外,它对所有数据执行命令,而不仅仅是不以 >.

开头的行

这是 GNU awk 中的一个:

$ awk -v seed=$RANDOM '                   # get some randomness from shell
function cmp_randomize(i1, v1, i2, v2) {  # random for traversal function
    return (2 - 4 * rand())               # from 12.2.1 Controlling Array Traversal
}                                         # of Gnu awk docs
BEGIN {
    srand(seed)                           # use the seed, Luke
    PROCINFO["sorted_in"]="cmp_randomize" # use above defined function
}
/^[^>]/ {                                 # if starts with anything but >
    split([=10=],a,"")                        # split to hash a
    for(i in a)                           # iterate a in random order
        printf "%s", a[i]                 # output
    print ""                              # newline
    next                                  # next record
}1' file                                  # output > starting records
>line1
CAB
>line2
DFE
>line3
GIH
>line4
LKJ

perlruby

$ # split// to get individual characters
$ # join "" to join characters with empty string
$ # if !/^>/ to apply the change only for lines not starting with >
$ # alternate: perl -MList::Util=shuffle -lne 'print /^>/ ? $_ : shuffle split//'
$ perl -MList::Util=shuffle -lpe '$_=join "", shuffle split// if !/^>/' ip.txt 
>line1
CBA
>line2
FED
>line3
IHG
>line4
JKL

$ # $_.chars to get individual characters
$ # * "" to join array elements with empty string
$ ruby -lpe '$_ = $_.chars.shuffle * "" if !/^>/' ip.txt 
>line1
BAC
>line2
EDF
>line3
GHI
>line4
JKL

awk + coreutils 方法:

awk '/^[^>]/{ system("echo ""| fold -w1 | shuf | tr -d 7\n7"); print ""; next }1' file

示例输出:

>line1
BAC
>line2
EDF
>line3
HGI
>line4
KLJ

对于GNU sed

$ cat filename.txt
>line1
ABC
>line2
DEF
>line3
GHI
>line4
JKL
$ sed -r "/^[^>]/s/.*/grep -o . <<< & |sort -R |tr -d '\n'/e" filename.txt
>line1
ABC
>line2
FDE
>line3
HGI
>line4
LKJ
$ sed -r "/^[^>]/s/.*/grep -o . <<< & |shuf |tr -d '\n'/e" filename.txt
>line1
BCA
>line2
FDE
>line3
HIG
>line4
JKL

编辑:sed 在所有 (GNU sed) 4.2.2 上都一样工作,我们可以通过删除 e 修饰符来打印 sed 生成的原始命令字符串:

sed -r '/^[^>]/s/.*/grep -o . <<< & |shuf |tr -d "\n"/' filename.txt
>line1
grep -o . <<< ABC |shuf |tr -d "
"
>line2
grep -o . <<< DEF |shuf |tr -d "
"
>line3
grep -o . <<< GHI |shuf |tr -d "
"
>line4
grep -o . <<< JKL |shuf |tr -d "
"

然后,seds命令的e修饰符会调用sh执行。 CentOS上的sh是符号link到bash,但在Ubuntu上是符号link到dashdash 可能不支持 <<< (here-string).

# on Ubuntu, enter into sh terminal:
$ grep -o . <<< JKL |shuf |tr -d '\n'
sh: 2: Syntax error: redirection unexpected
$ echo JKL |grep -o . |shuf |tr -d '\n'
KLJ

所以,我需要修改我的答案以同时适用于 bashdash:

$ sed -r '/^[^>]/s/.*/echo -n & |grep -o . |shuf |tr -d "\n"/e' filename.txt
>line1
ACB
>line2
DFE
>line3
IHG
>line4
LJK

简单说明:

  1. /^[^>]/: 强制 sed 处理以 (^) 开头且不是 > ([^>]) 的行。
  2. s/.*/echo -n & |grep -o . |shuf |tr -d "\n"/.*是整行,用&代替,所以&是整行,然后生成一个普通命令字符串 echo -n ORIGIN_LINE |grep -o . |shuf |tr -d "\n",可以随机排列一行。
  3. 最后,使用s命令的e修饰符来执行上面生成的普通命令字符串。

这可能适合您 (GNU sed):

sed '/^>/b;s/./&\n/g;s/.$//;s/.*/echo "&"|shuf/e' file

打印以 > 开头的行,但不处理。否则,在当前行的每个字符之间插入换行符并删除最后一个不需要的换行符。然后 echo 生成文件并通过 shuf 命令(必要时可以替换 sort -R)并打印结果。

N.B。此解决方案在替换命令上使用 GNU 特定的 e 标志,但是结果可以传递给 shell,如下所示:

sed '/^>/s/.*/echo "&"/;t;s/./&\n/g;s/.$//;s/.*/echo "&"|shuf/' file | sh