Bash 应用替换N次

Bash 应用替换N次,bash,sed,Bash,Sed,我有一根像 data1_data2_data3_data4@data5,data6 有时,data5包含下划线,而下划线恰好是字段分隔符。丑陋,我知道 我想通过以下方式阅读这些数据: IFS="_@," read d1 d2 d3 d4 d5 d6 <<< "$input" 一个选项可以是使用shell扩展${var%%pat}手动拆分匹配pat的最大后缀和${var#pat}拆分匹配pat的最短前缀 while IFS= read line; do tmpline

我有一根像

data1_data2_data3_data4@data5,data6
有时,data5包含下划线,而下划线恰好是字段分隔符。丑陋,我知道

我想通过以下方式阅读这些数据:

IFS="_@," read d1 d2 d3 d4 d5 d6 <<< "$input"

一个选项可以是使用shell扩展
${var%%pat}
手动拆分匹配pat的最大后缀和
${var#pat}
拆分匹配pat的最短前缀

while IFS= read line; do
    tmpline=$line
    d1=${tmpline%%_*} tmpline=${tmpline#*_}
    d2=${tmpline%%_*} tmpline=${tmpline#*_}
    d3=${tmpline%%_*} tmpline=${tmpline#*_}
    d4=${tmpline%%@*} tmpline=${tmpline#*@}
    d5=${tmpline%%,*} tmpline=${tmpline#*,}
    d6=${tmpline}

    printf "%s\n" "d1=$d1" "d2=$d2" "d3=$d3" "d4=$d4" "d5=$d5" "d6=$d6"
done <<< "$input"

您可以在流程替换中使用此
awk

input="data1_data2_data3_data4@d_a_t_a_5,data6"

IFS=, read d1 d2 d3 d4 d5 d6 < <(awk -F@ -v OFS=, -v n=3 '{
while (i++<n) sub(/_/, ",", $1)} 1' <<< "$input")

# check variable values
declare -p d1 d2 d3 d4 d5 d6
  • awk
    命令使用
    @
    作为字段分隔符
  • awk
    命令将
    \uu
    替换为
    仅在第一个字段中精确地
    n次

    • bash
      中,我将使用正则表达式

      $ cat input
      one_two_three_fourpt1_fourpt2@fivept1_fivept2,six
      $ regex='([^_]+)_([^_]+)_([^_]+)_(.+)@([^,]+).(.*)'
      $ while IFS= read -r line; do
      > [[ $line =~ $regex ]]
      > done < input
      $ printf '%s\n' "${BASH_REMATCH[@]}"
      one_two_three_fourpt1_fourpt2@fivept1_fivept2,six
      one
      two
      three
      fourpt1_fourpt2
      fivept1_fivept2
      six
      
      因为第二个只读调用有4个参数,
      f4
      将包含第三个
      \u
      后面的任何参数,而不会在其他
      \u
      上进一步拆分字段


      类似的正则表达式和两级拆分方案可用于支持对文件内容进行更高效迭代的语言中,而这(正如Nahuel Fouilleul指出的)
      bash
      并不能很快完成。(
      read
      逐字节读取其输入,而不是一次读取整个数据块,以避免读取的字节数超过仅消耗一行输入所需的字节数。)

      使用awk

      $ input="data1_data2_data3_data4@d_a_t_a_5,data6"
      $ awk -v RS='[@\n]' '{ if(NR % 2){ gsub(/_/, ","); ORS = "," } else ORS = "\n" } 1' <<< "$input"
      data1,data2,data3,data4,d_a_t_a_5,data6
      
      $input=“data1\u data2\u data3_data4@d_a_t_a_5,数据6“
      
      $awk-v RS='[@\n]'{if(NR%2){gsub(//,“,”);ORS=“,”}else-ORS=“\n”}1”如果一个字段中有@.\u……,
      你可以试试这个awk:

      echo "data1_data2@d_a_t_a_17,data3_data4@d_a_t_a_5,data6_data7" |
      awk '
      {
      i = split ( $0 , a , "_" )
      for ( j = 1 ; j <= i ; j++ )
        if ( a[j] !~ /@/ )
          print "d" ++k "==\"" a[j] "\""
        else
          {
            split ( a[j] , b , "@" )
            print "d" ++k "==\"" b[1] "\""
            sub ( ".*@" , "" , a[j] )
            while ( a[j] !~ "," )
              {
                c = c a[j] "_"
                j++
              }
              split ( a[j] , b , "," )
              c = c b[1]
              print "d" ++k "==\"" c "\""
              a[j] = b[2]
              j--
              c = ""
          }
      }'
      
      echo“数据1_data2@d_a_t_a_17,数据3_data4@d_a_t_a_5,数据6_数据7“|
      awk'
      {
      i=分割($0,a,“uu3;”)
      
      对于(j=1;j Hi Poshi)。我们如何区分
      data1_data2
      d_a_t_a_5
      中的下划线。后一个特定于大小写的字段中的下划线如何与环绕字段分隔?如果您希望提高效率,请避免使用速度较慢的bash-read-bultin。使用perl怎么样?@TrebuchetMS,data{1..4}没有下划线。在
      @
      之前,下划线是字段分隔符。在
      @
      之后,逗号是字段分隔符。@Poshi:但您在问题中提到,
      问题出现在data4包含下划线时,因此您可能在
      @
      之前也有
      @
      ,您不想拆分。理想情况下,请修复此问题无论什么过程或工具首先产生了这种令人愤怒的格式,“如果我需要重复5000次会发生什么?”在这里,它比调用sed效率更高,因为扩展是一个内置的,不会产生新的进程,但瓶颈是出于效率考虑的读取内置god点,但这不是重点。我正试图为手头的问题编写最紧凑、最清晰的代码。输入将始终是一行,特别是在4左右0字节。这是用于交互式工作。我很确定操作员不会担心再等待0.1秒。但我担心,如果有一天我必须使用最长的字符串执行类似操作。在你的问题中,你问如果你必须重复5000次,最紧凑的是sed版本,缺点是你正在为e启动一个新进程ach调用,与awk或perlRight相同,这就是为什么我只启动一个包含所有替换的
      sed
      进程:-)您仍然会逐行迭代文件(我想我应该在答案中包括).我的意思是:如果每行必须重复5000次转换,该怎么办?我试图避免重复regexp。问题中显示的一行比这两行更简单、更干净:-(这行有5000个不同的字段?我肯定会换一个不同的语言;
      bash
      不适用于那种数据处理!你可以在所有
      上拆分,然后重新合并拆分的任何后续部分。(以Python为例,类似于
      fields=line.split(“'');fields[5000]=''.''.join(fields[5001:]))
      )此解决方案适用于5000假设,但它与我正在寻找的简单、简单、简短的一行代码相去甚远。此外,它不仅转换下划线的前N个出现,而且转换到“@”,但这很容易解决。无论如何,您的代码解决了手头的问题,但比仅重复三次sed替换要详细得多:-(哈哈哈!重写得很好。我想这是离我的圣杯越来越近的地方。我很难过没有标志告诉解释器重复替换N次,我必须手动处理字段。但我同意你的观点,你希望它更详细:-)
      input="data1_data2_data3_data4@d_a_t_a_5,data6"
      
      IFS=, read d1 d2 d3 d4 d5 d6 < <(awk -F@ -v OFS=, -v n=3 '{
      while (i++<n) sub(/_/, ",", $1)} 1' <<< "$input")
      
      # check variable values
      declare -p d1 d2 d3 d4 d5 d6
      
      declare -- d1="data1"
      declare -- d2="data2"
      declare -- d3="data3"
      declare -- d4="data4"
      declare -- d5="d_a_t_a_5"
      declare -- d6="data6"
      
      $ cat input
      one_two_three_fourpt1_fourpt2@fivept1_fivept2,six
      $ regex='([^_]+)_([^_]+)_([^_]+)_(.+)@([^,]+).(.*)'
      $ while IFS= read -r line; do
      > [[ $line =~ $regex ]]
      > done < input
      $ printf '%s\n' "${BASH_REMATCH[@]}"
      one_two_three_fourpt1_fourpt2@fivept1_fivept2,six
      one
      two
      three
      fourpt1_fourpt2
      fivept1_fivept2
      six
      
      $ IFS="@" read -r first second <<< "$line"
      $ IFS=_ read -r f1 f2 f3 f4 <<< "$first"
      $ IFS=, read -r f5 f6 <<< "$second"
      
      $ input="data1_data2_data3_data4@d_a_t_a_5,data6"
      $ awk -v RS='[@\n]' '{ if(NR % 2){ gsub(/_/, ","); ORS = "," } else ORS = "\n" } 1' <<< "$input"
      data1,data2,data3,data4,d_a_t_a_5,data6
      
      echo "data1_data2@d_a_t_a_17,data3_data4@d_a_t_a_5,data6_data7" |
      awk '
      {
      i = split ( $0 , a , "_" )
      for ( j = 1 ; j <= i ; j++ )
        if ( a[j] !~ /@/ )
          print "d" ++k "==\"" a[j] "\""
        else
          {
            split ( a[j] , b , "@" )
            print "d" ++k "==\"" b[1] "\""
            sub ( ".*@" , "" , a[j] )
            while ( a[j] !~ "," )
              {
                c = c a[j] "_"
                j++
              }
              split ( a[j] , b , "," )
              c = c b[1]
              print "d" ++k "==\"" c "\""
              a[j] = b[2]
              j--
              c = ""
          }
      }'