GNU并行中包含空格和特殊字符的转义变量

问题描述

我尝试并行优化 PDF 文件。 (我用的是 Mac)

#!/bin/zsh

TMP_DIR=$(mktemp -d)
DOCUMENT="/some/path/with/sp aces/and/üö chars.pdf"

mkdir "$DOCUMENT"_split

#split pdf into single pages
/usr/local/bin/pdfseparate "$DOCUMENT" "$TMP_DIR/${$(basename $DOCUMENT)%.pdf}_%d.pdf"

find "$TMP_DIR" -mindepth 1 -maxdepth 1 -name "*.pdf" ! -print0 | parallel -0 -j+0 '/usr/local/bin/ps2pdf {} {.}_optimized.pdf && mv {.}_optimized.pdf $DOCUMENT_split/$(basename {});'

一切正常,只要路径中没有空格或特殊字符。 mv 命令失败:

usage: mv [-f | -i | -n] [-v] source target
       mv [-f | -i | -n] [-v] source ... directory

我尝试了以下有助于目录路径方法,但以相同的方式包装 $(basename {}) 不起作用。

mv {.}_optimized.pdf '\"$DOCUMENT\"'_split/$(basename {})

解决方法

GNU Parallel 的替换字符串将正确引用结果字符串。这意味着即使是疯狂的文件名也可以安全使用:

touch "  Spacey  My brother's 12\" records.txt"
find . -print0 | parallel -0 echo {} {.}

此保证不适用于变量,尤其不适用于命令模板中执行的命令的输出。

# This does not do what you expect
DOCUMENT="It's  \"two\"  spaces"
find . -print0 | parallel -0 echo $DOCUMENT $(basename {})

您可以使用替换字符串 {.} 代替 basename$DOCUMENT 更难每次都正确:

DOCUMENT="It's  \"two\"  spaces"
export DOCUMENT
find . -print0 | parallel -0 echo '"$DOCUMENT"' {.}

通常,创建一个 bash 函数并调用它会更容易:

doit() {
  f="$1"
  echo "$DOCUMENT" "$(basename "$f")"
}
export -f doit
export DOCUMENT
find . -print0 | parallel -0 doit

(PS:-j+0 多年来一直是默认设置)。

,

我突然想到,有比摆弄所有引用更容易的可能性:如果您将 DOCUMENT 设为环境变量,您可以让 all 展开parallel 调用的 shell:

export DOCUMENT

只是因为我不知道 parallel 正在使用什么 shell,我不会指望它运行 zsh,并且会准备参数以便它即使在 POSIX shell 中也能工作:

... parallel -0 -j+0 '/usr/local/bin/ps2pdf {} {.}_optimized.pdf && mv {.}_optimized.pdf "$DOCUMENT_split/$(basename "{}")";'

这样,parallel 执行的 shell 就能准确地看到两个单引号之间的内容。这意味着您甚至可以通过将合适的值替换为 DOCUMENT 和 {} 来测试该方法。