【问题标题】:optimize xargs argument enumeration优化 xargs 参数枚举
【发布时间】:2017-01-23 00:03:46
【问题描述】:

可以更好地优化 xargs 参数枚举的这种用法吗? 目的是在实际命令的中间注入单个参数。

我愿意:

echo {1..3} | xargs -I{} sh -c 'for i in {};do echo line $i here;done'

echo {1..3} | for i in $(xargs -n1);do echo line $i here; done

我明白了:

line 1 here
line 2 here
line 3 here

这是我需要的,但我想知道是否可以避免循环和临时变量?

【问题讨论】:

  • for i in {1..3}; do echo line $i here; done?
  • 你的问题真的不太清楚,看起来真的是个XY问题。你有什么具体的用例?很可能有比您要求的更好的设计。
  • @gniourf_gniourf - 只是寻找一种简洁的参数使用方式,在后续命令中的任何位置通过管道传递
  • 这并不是一个真正的特定用例。对我来说,这从一开始就看起来像是一个破碎的设计。而且,如果您没有在 shell 脚本中进行过试验,那么您会忽略很多警告(其中大部分来自您显然混合了代码和数据的事实)。从这里开始,一种可能的情况是,你会错误地学习糟糕的技术,这会比你预期的更快地咬你一口。
  • @gniourf_gniourf - 感谢您的宝贵意见。您介意向我介绍一个资源,在那里我可以学习在 shell 脚本上下文中适当分离代码和数据吗?

标签: linux bash xargs


【解决方案1】:

您需要用换行符分隔xargs 的输入:

echo {1..3}$'\n' | xargs -I% echo line % here

对于数组扩展,您可以使用printf:

ar=({1..3})
printf '%s\n' "${ar[@]}" | xargs -I% echo line % here

(如果只是为了输出,可以不用xargs

printf 'line %s here\n' "${ar[@]}"

)

【讨论】:

  • 我不知道大括号扩展处理前缀和后缀。很高兴知道。值得一提的是,这在涉及其他类型扩展(例如扩展数组中的所有元素)的其他场景中不起作用。
  • 请注意echo {1..3}$'\n' 扩展为echo 1$'\n' 2$'\n' 3$'\n',因此echo 看到三个参数。现在echo 打印其参数用空格分隔,因此23 将以空格为前缀,并且您还将得到一个空尾行(因为echo还在末尾添加换行符)。如果以后您想将(GNU)xargs-d\\n 一起使用,这将导致问题。 printf 版本显然更优越,更惯用。
【解决方案2】:

尝试不使用xargs。在大多数情况下,xargs 太过分了。 根据您真正想要的,您可以选择类似的解决方案

# Normally you want to avoid for and use while, but here you want the things splitted.
for i in $(echo {1 2 3} );do 
   echo line $i here;
done

# When you want 1 line turned into three, `tr` can help
echo {1..3} | tr " " "\n" | sed 's/.*/line & here/'

# printf will repeat itself when there are parameters left
printf "line %s here\n" $(echo {1..3})

# Using the printf feature you can avoid the echo
printf "line %s here\n"  {1..3}

【讨论】:

    【解决方案3】:

    也许是这个?

    echo {1..3} | tr " " "\n" | xargs -n1 sh -c ' echo "line $0 here"'
    

    tr 用换行符替换空格,所以xargs 看到三行。如果有更好(更高效)的解决方案,我不会感到惊讶,但这个解决方案非常简单。

    请注意,我已经修改了我之前的答案,删除了 {} 的使用,这是 cmets 中建议的,以消除潜在的代码注入漏洞。

    【讨论】:

    • 更习惯使用printf '%s\n' {1..3}
    • 完全正确。我不会更新我的答案,另一个已经更好更完整了,而我的虽然不是最好的,但也没有错。
    • 请注意,您在 sh 代码中使用 {} 是一件非常糟糕的事情:您是在将代码与数据混合! (这是问题的主要来源之一,也是你应该认识到并摆脱的可怕反模式)。确实,很容易伪造会产生任意代码执行的数据,例如printf '%s\n' 'one; rm -rf some_precious_directory #' | xargs -I{} sh -c 'echo line {} here;',你会得到一些惊喜!您必须将 data 作为 data. 处理。为此:... | xargs -n1 sh -c ' echo "line $1 here"' shfind-exec sh -c '... {} ...' \; 也经常出现同样的错误
    • @gniourf_gniourf 我想修改我的答案以纠正这个问题,但我无法获得您建议 xargs 使用“printf”或基于“tr”的解决方案的代码( $1 扩展为空),我打印的只是“这里的行”。是不是我做错了什么?
    • @gniourf_gniourf 实际上,如果我将 1 美元替换为 0 美元,它会起作用。可以吗?
    【解决方案4】:

    GNU sed 有一个不为人知的特性。您可以将e 标志添加到s 命令,然后sed 执行模式空间中的任何内容,并用该命令的输出替换模式空间。

    如果您真的只对 echo 命令的输出感兴趣,您可以试试这个 GNU sed 示例,它消除了临时变量、循环(以及 xargs):

    echo {1..3} | sed -r 's/([^ ])+/echo "line \1 here"\n/ge
    
    • 它获取一个标记(即任何由空格分隔的标记)
    • 将其替换为echo "line \1 here"\n 命令,将\1 替换为令牌
    • 然后执行回显
    • 将 echo 命令的输出放回模式空间
    • 这意味着它输出三个回显的结果

    但获得所需输出的更好方法是跳过执行并直接在 sed 中进行转换,如下所示:

    echo {1..3} | sed -r 's/([^ ])+ ?/line \1 here\n/g' 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-03
      • 2010-09-16
      • 1970-01-01
      • 2022-10-06
      • 1970-01-01
      相关资源
      最近更新 更多