我有一个文件,其中包含尖括号中的相对路径,例如以下内容(例子.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 sed
–sed
阅读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"