Linux shell命令用文件内容替换路径

Linux shell命令用文件内容替换路径

我有一个文件,其中包含尖括号中的相对路径,例如以下内容(例子.txt):

Some content containing <../another.txt> file

然后在父目录中,文件另一个.txt

another

我可以使用什么 Linux 命令行来生成示例_已处理.txt<path>用指定路径下的文件内容替换token?例如,我想要一个命令来摄取例子.txt并产生示例_已处理.txt内容如下:

Some content containing another file

请注意,我不关心生成的文件中是否有多余的换行符,因此以下输出也是可以接受的(这只是一个示例,任何多余的空格都是可以接受的):

Some content containing
another
file

我有一个 bash 循环,可以将文件内容读入变量,但同样,不知道这是否有助于我执行替换:

cp example.txt example_processed.txt
grep -oP '<\K.*(?=>)' example.txt | while read -r REPL_PATH ; do
    local CONTENTS=$(<"$REPL_PATH")
    # TODO: How do I use this?  The following is what I want to work:
    # sed "s/<$REPL_PATH>/$CONTENTS/g"
    echo "$REPL_PATH: $CONTENTS"
done

这是产生最接近结果的方法,但需要另一个.txt位于同一目录中:

sed -e '/<\(.*\)>/{' -e 's/<.*>//' -e 'r another.txt' -e '}' -i example.txt

上述输出:

Some content containing file
another

问题:

  • 我如何指定替换路径为../另一个.txt
  • 我怎样才能取代文字另一个.txt在上面的命令中,使用捕获组 #1 的结果?例如,sed -e '/<\(.*\)>/{' -e 's/<.*>//' -e 'r \1' -e '}' -i example.txt
  • 如何移动替换字符串之间“包含”和“文件”等词语,而不是“文件”这个词?

答案1

我的想法是将输入文件转换为以下形式的 shell 脚本:

cat <<EOF$
EOF$

其中是输入文件的原始内容,但被<pathname>替换$(cat pathname),因此当脚本被 shell 解释时,它是一个命令替换,将被 的输出替换cat pathname

这是命令:

<example.txt sed '
   s/[$\\`]/\\&/g
   s/<\([^<>]*\)>/$(\n$$\1\n)/g
   1 i cat <<EOF$
   $ a EOF$
' | sed '
   /^\$\$/ {
      s/[^$\\`]/\\&/g
      s/^\$\$/cat -- /
      }
' | sh >example_processed.txt

一步步:

  • <example.txt sedsed阅读example.txt并执行以下操作:

    • s/[$\\`]/\&/g – 转义每一个$\`,否则它们在我们的文档中会很特殊;
    • s/<\([^<>]*\)>/$(\n$$\1\n)/g– 将 和 之间的每个字符串(包括<和 )转换为>(其中 不包含<>,因此是非贪婪的)
      $(
      $$string
      )
      
    • 1 i cat <<EOF$cat <<EOF$–在第一行之前插入;
    • $ a EOF$– 附加EOF$在最后一行之后。
  • | sed– 第二个sed读取第一个,然后

    • /^\$\$/– 标识以 开头的行$$(请注意,它们必须来自第一行sed,因为原始文件中的每个行$前面现在都带有反斜杠),并且:
      • s/[^$\\`]/\&/g– 除了$\或之外的每个字符`都用反斜杠转义(排除的字符在适当的位置已经转义)
      • s/^\$\$/cat -- /– 并且前导$$被替换为cat --
  • | sh >example_processed.txt– POSIX shell 解释生成的脚本并写入example_processed.txt

sh您的示例文件将以以下脚本形式呈现:

cat <<EOF$
Some content containing $(
cat -- \.\.\/\a\n\o\t\h\e\r\.\t\x\t
) file
EOF$

笔记:

  • EOF$而不是传统的EOF,因此原始文件中没有任何内容可以干扰。即使EOF$原始文件中有 ,在脚本中它也会是EOF\$
  • 路径名中的换行符不受支持,<并且相应的换行符>必须位于同一行输入中,我们的代码才能正常工作。
  • 支持其他字符。../another.txt脚本中的路径名 ( 在示例中 ) 已完全转义 (逐个字符),因此即使您使用带有空格、星号或其他字符的路径名,它也是安全的。
  • $(…)删除尾随的换行符,这通常没问题。
  • --解释如下:--(双破折号)是什么意思?
  • 中的相对路径<…>将根据 的工作目录进行解析sh,而不是根据包含输入文件的目录。在我们的示例中,它是同一个目录,但一般来说目录可能不同。如果您想根据输入文件的目录解析相对路径,那么您必须sh在这个确切的目录中运行,就像我们一样。
  • 输出将转到example_processed.txt与 故意使用不同名称的example.txt不要将输出重定向到你正在读取的文件

最终结果为example_processed.txt

Some content containing another file

答案2

以下是我最终用 Bash 脚本编写的内容,因为它更容易理解和维护:

#!/bin/bash

# Note that the following assumes the script is running in the
# same directory as the input file, so it can handle relative paths

local TEMPLATE="example.txt"
local GENERATED="%{TEMPLATE%.txt}_processed.txt"
rm -f "$GENERATED"

# Read the template file line-by-line
while IFS='' read -r LINE; do
    # Determine whether a line includes a link to another file
    if [[ $LINE =~ ^(.*)\<(.+)\>(.*)$ ]]; then
        # If the other file doesn't exist, error out
        if [ ! -f "${BASH_REMATCH[2]}" ]; then
            echo "Unable to include '${BASH_REMATCH[2]}` in '$TEMPLATE'" >&2
            exit 1
        fi

        # Replace the file path with the contents of the file
        echo -n "${BASH_REMATCH[1]}" >> "$GENERATED"
        cat     "${BASH_REMATCH[2]}" >> "$GENERATED"
        echo    "${BASH_REMATCH[3]}" >> "$GENERATED"
    else
        # Copy the line as-is
        echo "$LINE" >> "$GENERATED"
    fi
done < "$TEMPLATE"

相关内容