我有一个markdown文件目录,我试图通过以下方法完成该任务:
我已经关闭了,但是下面的代码仅提取第一个markdown文件的文件名,并将变量应用于文件中的所有字符串。到目前为止,这是我的工作代码:
#!/bin/bash
for file in /home/user/dir/*; do
str="somestring"
filename=$(basename $file)
fn="$(echo "${filename%.*}")"
find ./ -type f -exec sed -i '' -e "s/${str}/${fn}/g" {} \;
done
假设降价文件如下所示:
123456789.md
,位于/home/user/dir/123456789.md
的其他几个.md文件中,它们带有其他随机数字名称。
.md文件的结构类似于:
---
layout: default
date: 2010-03-28
original: /orig/somestring.jpg
thumbnail: /thumb/somestring_thumb.jpg
permalink: /images/somestring/
---
我的目标是使脚本根据.md文件本身的文件名使每个文件看起来像这样:
---
layout: default
date: 2010-03-28
original: /orig/123456789.jpg
thumbnail: /thumb/123456789_thumb.jpg
permalink: /images/123456789/
---
您是否对编辑sed调用的最佳方法或编写此方法的其他方法有任何想法?有时候,在sed中,sed返回sed: RE error: illegal byte sequence
,但是无论如何都使用了字符串的重命名,即使它是错误的字符串。
答案 0 :(得分:1)
考虑使用以下相当健壮的解决方案。这样可以确保您给定的搜索字符串和/或Markdown文件名中的任何可能被解释为basic regular expression(BRE)元字符的字符都被视为sed
替换中的文字。
#!/usr/bin/env bash
target_dir=/path/to/dir
search='somestring'
search_escaped=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"$search")
while read -rd ''; do
base=$(basename -- "$REPLY")
replace_escaped=$(sed 's/[&/\]/\\&/g' <<<"${base%.*}")
sed -i '' -e 's/'"$search_escaped"'/'"$replace_escaped/g"'' "$REPLY"
done < <(find $target_dir -depth 1 -type f -name '*.md' -print0)
说明:
应将target_dir
变量的值定义为要在其中进行搜索的目录的路径名。例如,在问题中指定的/home/user/dir
。
search
变量的值应更改为要在减价(.md
)文件中搜索的字符串,并且必须将其括在单引号({ {1}}。
读取的行;
'...'
转义您的search_escaped=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"$search")
字符串中可能存在的潜在BRE元字符,并将结果分配给名为search
的新变量。
之所以这样做,是因为最终定义的搜索字符串将与sed的s
command(即 search_escaped
)一起用作 search 字符串。基本上,给定s/regexp/replacement/flags
字符串中的每个字符都放在其自己的字符集search
表达式中,以将其视为文字,但转义([...]
)字符会转义,为^
。有关更多详细信息,请参见this answer。
这意味着我们可以提供一个\^
字符串,例如search
,即带有许多元字符的字符串,它们将被当作文字,并防止我们的程序出错。
使用find
实用程序,我们定义以下命令来获取给定s$o.m *e[s\t^ring
内所有.md
文件的路径名:
target_dir
find $target_dir -depth 1 -type f -name '*.md' -print0
部分确保我们仅在顶层找到文件。但是,如果您想递归地递归给定的目录树,则可以将其删除-通过删除它,您还将在给定目录的子目录中包含任何-depth 1
文件,这些文件的目录级别很深。
.md
部分确保我们仅包括Markdown文件(-name '*.md'
),并排除给定.md
中可能存在的任何其他文件。
target_dir
中包含的find
部分(称为process substitution)和前面<( ... )
redirects的路径名是{{ 1}}到<
。
find
循环read生成stdin
命令的结果,即找到的每个while
文件的路径名。
在find
循环的主体中,我们执行以下任务:
我们从每个路径名获得 basename (注意:.md
是与while
关联的内置变量-在这种情况下,它持有对路径名的引用在循环的每一圈中):
$REPLY
该行显示为:
while
转义base=$(basename -- "$REPLY")
可能会被视为占位符的字符,例如文件名中的replace_escaped=$(sed 's/[&/\]/\\&/g' <<<"${base%.*}")
。例如;如果文件名为sed
,当我们用它替换\1
字符串时会失败-但是,这样做可以防止这种情况。同样,请参阅this answer以获得更多详细信息。
somefile\1\2\3.md
部分利用parameter expansion从search
变量的值(即文件名/基名)中省略了文件扩展名部分(即${base%.*}
)。
最后,我们将Markdown文件中可能存在的所有 search 字符串实例(即.md
变量的值)替换为{{ 1}}变量(即不带文件扩展名的文件名)。
base
已知问题::基本名称的任何部分都可能包含换行符($search_escaped
),尽管此解决方案可以使用以下方式正确处理此类路径名的发现here中介绍的方法-当文件名包含换行符时,它当前不执行字符串替换。
答案 1 :(得分:0)
如果我正确理解,则可以进行以下操作:
#!/bin/bash
for file in /home/user/dir/*; do
str="somestring"
filename=$(basename "$file")
fn=${filename%.*}
LANG=C sed -i '' -e "s/${str}/${fn}/g" "$file"
done
问题是您正在find & sed
循环中执行for
,这会替换不相关文件中的字符串。
LANG=C
之前的sed
是解决sed: RE error: illegal byte sequence
问题的常用解决方法。