如何使用 awk 进行分割并转义所有特殊字符

如何使用 awk 进行分割并转义所有特殊字符

我正在尝试使用 split 创建一个 char 数组,到目前为止它有效。

问题是当输入字符串中的任何字符前面带有 \ 时。发生的情况是 \ 不会被视为字符,因为它会转义后面的字符并丢失,不会在数组中被考虑。

目标是将所有内容存储在 charArray 中以供以后使用。

function getLineChars {
   l=1
   for line in ${fileLinesArray[@]}; do
      charArray=$(echo | awk -v str="${line}" '{
         split(str, lineChars, "")
         for (i=1; i<=length(str); i++) {
            printf("%s ", lineChars[i])
         }
      }')
      l=$(($l+1))
      echo "${charArray[@]}"
   done
}

因此,主要是将每个特殊或奇怪的字符打印到数组中,除了这种情况:

3\zKhj awk: warning: escape sequence `\z' treated as plain `z'

数组结果如下:

3 z K h j

缺少 \ 字符,需要将其包含在数组中。

关于这个还能做什么?尝试使用 awk 可以吗,或者您会建议不同的东西吗?

提前致谢。

答案1

如果您确实需要使用awkthen feed${line}作为此处字符串:

function getLineChars {
   l=1
   for line in "${fileLinesArray[@]}"; do
      charArray=$( awk '{ split($0, lineChars, "")
                          for (i=1; i<=length($0); i++) {
                              printf("%s ", lineChars[i])
                          }
                        }' <<< "${line}" )
      l=$(($l+1))
      echo "${charArray[@]}"
   done
}

进行试驾:

$ fileLinesArray=( '3\zKhj' )
$ getLineChars
3 \ z K h j

但是,里面到底是什么charArray[@]

$ typeset -p charArray
declare -- charArray="3 \\ z K h j "

它实际上是一个字符串,带有一个尾随空格。

如果您确实想要一个字符数组,请替换charArray=$( awk ... )charArray=( $( awk ... ) );进行更改并进行试驾:

$ getLineChars                                                                           
3 \ z K h j

$ typeset -p charArray
declare -a charArray=([0]="3" [1]="\\" [2]="z" [3]="K" [4]="h" [5]="j")

现在我们有了一个实际的字符数组。


我可能会选择更简单的东西,例如:

function getLineChars {
   l=1
   for line in "${fileLinesArray[@]}"; do
      mapfile -t charArray < <( grep -o . <<< "${line}" )
      l=$(($l+1))
      echo "${charArray[@]}"
   done
}

笔记:更新为使用mapfile(同义词readarray;感谢埃德·莫顿)。

进行试驾:

$ getLineChars
3 \ z K h j

$ typeset -p charArray
declare -a charArray=([0]="3" [1]="\\" [2]="z" [3]="K" [4]="h" [5]="j")

或者我们可以$( grep ... )通过正则表达式和BASH_REMATCH[]数组消除子进程调用:

getLineChars() {
    l=1
    for line in "${fileLinesArray[@]}"; do 
        [[ "${line}" =~ ${line//?/(.)} ]] && charArray=( "${BASH_REMATCH[@]:1}" )
        l=$(($l+1))
        echo "${charArray[@]}" 
    done
}

在哪里:

  • ${line//?/(.)}- 用文字字符串替换每个字符,(.)从而为每个字符提供一个捕获组(注意:做不是将其用双引号括起来)
  • "${BASH_REMATCH[@]:1}"- 获取以index == 1 开头并一直到数组末尾的所有数组条目

进行试驾:

$ getLineChars
3 \ z K h j

$ typeset -p charArray
declare -a charArray=([0]="3" [1]="\\" [2]="z" [3]="K" [4]="h" [5]="j")

$ typeset -p BASH_REMATCH
declare -a BASH_REMATCH=([0]="3\\zKhj" [1]="3" [2]="\\" [3]="z" [4]="K" [5]="h" [6]="j")

答案2

对空 FS 进行拆分split(str, lineChars,"")是未定义的行为,因此它会在不同的 awks 中执行不同的操作,使用-v将变量的值传递给 awk 会按设计扩展转义序列,这不是您想要的(请参阅如何在 awk 脚本中使用 shell 变量用于替代方案),并且使用 echo 和管道会带来不必要的开销和脆弱性(将根据您使用的字符和 echo 版本而中断)。

charArray在你的代码中:

charArray=$(echo | awk '...')

是一个标量,而不是一个数组,我认为你的意思是:

charArray=( $(echo | awk '...') )

但是使用命令输出填充数组array=( command )会将命令输出暴露给 shell 以进行通配和文件名扩展,因此永远不要对任何命令执行此操作,readarray而是使用,例如尝试这两个:

$ line='a*b c'; array=( $(grep -o . <<<"$line") )
declare -p array
<output will not include the `*` or blank char from `$line` but will include the names of all files in your current directory>

$ line='a*b c'; readarray -t array < <(grep -o . <<<"$line")
$ declare -p array
declare -a array=([0]="a" [1]="*" [2]="b" [3]=" " [4]="c")

bash因此,如果您打算使用调用 awk 的 shell 循环来执行此操作,则为了稳健性和可移植性(假设您使用的是 shell),请执行此操作:

$ line='3\zK*h jÃk'

$ readarray -t charArray < <(
    awk '
        BEGIN {
            line = ARGV[1]
            ARGV[1] = ""
            lgth = length(line)
            for (i=1; i<=lgth; i++) {
                print substr(line,i,1)
            }
        }
    ' "$line"
)

$ declare -p charArray
declare -a charArray=([0]="3" [1]="\\" [2]="z" [3]="K" [4]="*" [5]="h" [6]=" " [7]="j" [8]="Ã" [9]="k")

但几乎肯定有一种更好的方法来完成您想做的任何事情,而不是让 shell 循环一次调用 awk 一行,如果您需要解决更大问题的帮助,请发布带有示例输入/输出的新问题。

哦,还有永远不要命名变量l因为它看起来太像数字了1,所以混淆了你的代码,并且你的函数还存在一些其他问题,将其复制/粘贴到http://shellcheck.net将告诉您并帮助您修复。

答案3

如果您想通过在 awk 的代码字符串中附加值来将变量传输到 awk:

awk 'BEGIN {var="'"$BASH_variable"'"}

您可以使用我的库中的此函数:


declare g_RV  #-- g_RV ... global return value

#-- call:        g_serialize_STR_ForAWK  [string to serialize STR] [option bINT]
#-- description: converts a string to combine it with an awk variable declaration: 'BEGIN { var="'[serialized string STR]'" ..}'
#--              '\' becomes '\\', '"' becomes '\"', $'\n' becomes '\n' 
#-- parameters:  $1 ... string to serialize STR - a string you want to transmit to awk per variable declaration (var="...")
#--              $2 ... option bINT optional - convert it with bash (0), convert it with sed (1), Standard (0)
#-- returnValue: written to g_RV - the converted string STR
#-- depends on:  variables - g_RV
function g_serialize_STR_ForAWK ()
    {
    local -i option=$2
    
    #-- use sed for converting
    if ((option)); then
        g_RV=$(sed -z 's/\\/\\\\/g; s/"/\\"/g; s/\n/\\n/g' <<< $1";")    
        g_RV=${g_RV:0:-1}
    #-- use bash for converting    
    else
        g_RV=${1//'\'/'\\'}; g_RV=${g_RV//'"'/'\"'}; g_RV=${g_RV//$'\n'/'\n'}
    fi
    }

答案4

使用珀尔和/或保持反斜杠转义字符完整

  • 珀尔解决方案:
~$ echo -n '3\zKh j' | perl -ne 'print split /(?<!\\)/'
3\zKh j

#visualize split with Data::Dumper module

~$ ~$ echo -n '3\zKh j' | perl -MData::Dumper -ne 'print Dumper split /(?<!\\)/'
$VAR1 = '3';
$VAR2 = '\\z';
$VAR3 = 'K';
$VAR4 = 'h';
$VAR5 = ' ';
$VAR6 = 'j';

#and also Unicode (add `-CSDA` to command line)

~$ echo -n '3\zKh jÃkΣ' | perl -CSDA -MData::Dumper -ne 'print Dumper split /(?<!\\)/'
$VAR1 = '3';
$VAR2 = '\\z';
$VAR3 = 'K';
$VAR4 = 'h';
$VAR5 = ' ';
$VAR6 = 'j';
$VAR7 = "\x{c3}";
$VAR8 = 'k';
$VAR9 = "\x{3a3}";

  • (语言以前称为Perl6)解决方案:
~$ echo -n '3\zKh j' | raku -ne '.comb(/ \\? . /).print'
3 \z K h   j

#visualize split with `raku` built-in

~$ echo -n '3\zKh j' | raku -ne '.comb(/ \\? . /).raku.print'
("3", "\\z", "K", "h", " ", "j").Seq

#and also Unicode (enabled by default)

~$ echo -n '3\zKh jÃkΣ' | raku -ne '.comb(/ \\? . /).raku.print'
("3", "\\z", "K", "h", " ", "j", "Ã", "k", "Σ").Seq

Perl 参考资料:
https://perldoc.perl.org
https://www.perl.org

乐库参考资料:
https://docs.raku.org
https://raku.org

相关内容