Xargs 提取文件名

Por*_*ine 4 find xargs awk echo

我想找到.html一个文件夹中的所有文件并将其附加[file](./file.html)到另一个名为index.md. 我尝试了以下命令:

ls | awk "/\.html$/" | xargs -0 -I @@ -L 1 sh -c 'echo "[${@@%.*}](./@@)" >> index.md'
Run Code Online (Sandbox Code Playgroud)

但是它不能@@在命令内部替换?我究竟做错了什么?

注意:文件名可以包含空格等有效字符


澄清:

index.md将每一行,[file](./file.html)其中文件是文件夹中的实际文件名

Sté*_*las 13

做就是了:

for f in *.html; do printf '%s\n' "[${f%.*}](./$f)"; done > index.md
Run Code Online (Sandbox Code Playgroud)

当没有文件时,使用set -o nullglob( zsh, yash) 或shopt -s nullglob( bash) for*.html扩展为空而不是*.html(或在 中报告错误zshhtml。使用zsh,您还可以使用*.html(N)ksh93 ~(N)*.html

或者与一个printf电话zsh

files=(*.html)
rootnames=(${files:r})
printf '[%s](./%s)\n' ${basenames:^files} > index.md
Run Code Online (Sandbox Code Playgroud)

请注意,根据您使用的 Markdown 语法,如果文件名包含一些有问题的字符,您可能需要对标题部分进行 HTML编码并对 URI 部分进行 URI 编码。不这样做甚至可能最终根据上下文引入一种形式的 XSS 漏洞。使用 ksh93,您可以使用:

for f in *.html; do
  title=${ printf %H "${file%.*}"; }
  title=${title//$'\n'/"<br/>"}
  uri=${ printf '%#H' "$file"; }
  uri=${uri//$'\n'/%0A}      
  printf '%s\n' "[$title]($uri)"
done > index.md
Run Code Online (Sandbox Code Playgroud)

其中%H¹ 进行 HTML 编码和%#HURI 编码,但我们仍然需要分别处理换行符。

或与perl

perl -MURI::Encode=uri_encode -MHTML::Entities -CLSA -le '
  for (<*.html>) {
     $uri = uri_encode("./$_");
     s/\.html\z//;
     $_ = encode_entities $_;
     s:\n:<br/>:g;
     print "[$_]($uri)"
  }'
Run Code Online (Sandbox Code Playgroud)

使用<br/>的换行符。您可能想要使用 ? 取而代之或更普遍地决定不可打印字符的某种形式的替代表示。

您的代码中有几处错误:

  • 解析输出 ls
  • $在双引号内使用意为文字
  • 使用awk的东西,grep可以做(没有错本身,而是矫枉过正)
  • 使用xargs -0时输入不是NUL分隔
  • -I与 冲突-L 1-L 1是为每行输入运行一个命令,但将行中的每个单词作为单独的参数传递,同时-I @@为每行输入运行一个命令,并使用整行(减去尾随空格,并且引用仍在处理)用于替换@@.
  • {}在(命令注入漏洞)的代码参数中使用sh
  • 在 中shvarin${var%.*}是一个变量名,它不适用于任意文本。
  • 使用echo任意数据。

如果你想使用xargs -0,你需要这样的东西:

printf '%s\0' * | grep -z '\.html$' | xargs -r0 sh -c '
  for file do
    printf "%s\n" "[${file%.*}](./$file)"
  done' sh > file.md
Run Code Online (Sandbox Code Playgroud)
  • 更换lsprintf '%s\0' *得到NUL分隔输出
  • awk使用grep -z(GNU 扩展)来处理以 NUL 分隔的输出
  • xargs -r0(GNU扩展)没有任何-n/ -L/ -I,因为虽然我们在产卵sh,我们不妨把它处理尽可能多的文件成为了可能
  • 已将xargs单词作为额外参数传递给sh(成为内联代码内的位置参数),而不是代码参数内。
  • 这意味着我们可以更轻松地将它们存储在变量中(这里for file do默认情况下使用which 循环位置参数),因此我们可以使用${param%pattern}参数扩展运算符。
  • 使用printf代替echo.

不用说,使用它而不是像上面的例子那样for直接在*.html文件上循环是没有意义的。


¹尽管我的 ksh93 版本中的多字节字符似乎无法正常工作(GNU 系统上的 ksh93u+)

  • 我*认为*这仍然是 OP 想要的。OP 使用 `&gt;&gt;` 是因为他在循环内使用它,而循环后的这个答案和同一脚本的第二次运行对我来说没有太大意义。 (2认同)

pLu*_*umo 9

不要解析 ls
你不需要xargs这个,你可以使用find -exec.

尝试这个,

find . -maxdepth 1 -type f -name "*.html" -exec \
    sh -c 'f=$(basename "$1"); echo "[${f%.*}]($1)" >> index.md' sh {} \;
Run Code Online (Sandbox Code Playgroud)

如果您使用xargs,请使用这个非常相似的版本:

find . -maxdepth 1 -type f -name "*.html" -print0 | \
    xargs -0 -I{} sh -c 'f=$(basename "$1"); echo "[${f%.*}]($1)" >> index.md' sh {} \;
Run Code Online (Sandbox Code Playgroud)

另一种不运行xargs或的方法-exec

find . -maxdepth 1 -type f -name "*.html" -printf '[%f](./%f)\n' \
    | sed 's/\.html\]/]/' \
    > index.md
Run Code Online (Sandbox Code Playgroud)

  • 这是取自 [this answer](https://unix.stackexchange.com/a/156010/236063)。请参阅那里的评论和 `man sh` -&gt; `-c` 以了解为什么需要这样做的文档。 (2认同)