Sed 给出的错误信息:sed: 无法读取文件: 没有那个文件或目录。

82

我有以下的 bash 脚本,对于找到的每个图片都会重复执行。它需要遍历所有的 htmlcssjs 文件,并在文件中替换所有出现的图片。

 for image in app/www/images-theme-dark/*.png
    do
        echo "installing icon" $image

        # extract filename from iconpath
        iconfile=$(basename $image)
        iconPath="images/"$(basename $image)

        # replace paths in all files containing icon paths
        find app/www -type f \( -name "*.html" -or -name "*.css" -or -name "*.js" \
                            -or -name "*.appcache" \)  \
            -exec sed -i '' -e 's|$iconPath|images-theme-dark/$iconfile|g' "{}" \;

    done

然而当我运行脚本时,sed会出现以下错误信息:sed: can't read : No such file or directory。在StackOverflow上,我找到了解决方法sed: can't read : No such file or directory ,但我已经在{}周围加上了引号。当我使用echo命令输出sed命令并手动在命令行中执行时,没有发生错误。 我正在Raspbian GNU/Linux 8.0(jessie)上使用GNU sed v4.2.2。有谁能看出这里可能出了什么问题吗?

1
顺便提一下:请参阅:Bash 中单引号和双引号的区别 - Cyrus
3
sed -i 后面的 '' 是指定备份文件的后缀名,如果你不想保留备份文件,可以将其设置为空字符串。 - melpomene
6
“-i”参数的语法是GNU sed和Mac OS中的sed之间的一个区别。 - Cyrus
4
@osi: -i 的意思是“就地编辑”,也就是直接在文件中进行编辑。-i '' 的意思是“编辑一个文件,该文件的名称为空字符串”。由于您很可能实际上没有一个名称为空字符串的文件,因此 sed 报错无法读取该文件。 - AlexP
1
@Yunnosh:语法要求在-i之后立即添加备份副本的后缀,而不是作为单独的参数。例如,sed -i.bak将直接编辑文件并通过追加.bak来创建备份。如果使用单独的参数,则会将其视为要编辑的文件名。已在GNU sed 4.2.2上进行了测试。 - AlexP
显示剩余6条评论
4个回答

121

(从评论中编译答案,专业技能由melpomene和AlexP提供。)

sed -i后面的 '' 是什么意思?

-i 表示原地编辑,即直接在文件中进行编辑。
-i '' 表示在名称为空字符串的文件中进行编辑。
因为可能不存在名称为空字符串的文件,所以sed会抱怨它无法读取它。

注 1平台依赖性:
-i 的语法是GNU sed和mac os上的sed之间的一个区别。

注意 2参数的“通常”顺序:
-e 开关指示sed代码可以出现在文件名之间。
这是一个陷阱(我曾尴尬地被抓住),让您在 sed 命令行中的位置产生期望的错误。
它允许
sed -i filename -e "expression" AnotherFileName
这是一个不经意间掩盖的版本:
sed -i'NoExtensionGiven' "expression" filename AnotherFileName


99

为支持OSX和Linux,我使用一个简单的if检查来判断bash脚本是否在OSX或Linux上运行,并根据情况调整命令的-i参数。

对于支持OSX和Linux,我使用一个简单的if条件语句来检查bash脚本是否在OSX或Linux上运行,并根据结果调整命令的-i参数。

if [[ "$OSTYPE" == "darwin"* ]]; then
  sed -i '' -e 's|$iconPath|images-theme-dark/$iconfile|g' "{}"
else
  sed -i -e 's|$iconPath|images-theme-dark/$iconfile|g' "{}"
fi

3
Linux 的命名规则在 Windows 上也适用于 msys。感谢 Acidic 提供的建议。 - David Scott Kirby

10

在我的bash脚本中,我使用类似以下的语句(支持MacOS和Linux):

SEDOPTION=
if [[ "$OSTYPE" == "darwin"* ]]; then
  SEDOPTION="-i ''"
fi

sed $SEDOPTION "/^*/d" ./file

2
请解释一下这个回答相比于现有答案(特别是@Acidic9的答案)提供了哪些额外的见解。我没有看到你的回答和那个回答之间的功能区别。此外,“我使用类似这样的东西”并没有对这个仅包含代码的回答进行任何解释。简而言之,请让您的贡献更加明显,以避免给人抄袭并最小化、无关紧要地修改的印象。 - Yunnosch
2
storenth的答案更具可移植性,使用越多的sed命令就越好,尽管我需要在非macOS上使用-i选项。话虽如此,这个解决方案在我的macOS上有一个奇怪的问题,即将$SEDOPTION中的引号附加到文件名后面。我不得不采取以下hackery来防止这种情况发生...我尝试了许多其他方法,比如使用xargs进行管道传输,交换单引号和双引号,反引号等等,但是这是对我有效的方法(\x27代表单引号):if [[ "$OSTYPE" == "darwin"* ]]; then SEDOPTION='-i \x27\x27'; else SEDOPTION='-i'; fi; - Kerry Johnson
这假设sed是基于macOS的基本版本,并且如果不是(例如,如果sed来自MacPorts),可能会出现问题。一个牢固的解决方案更加复杂(仅仅检查sed的路径可能不够……)。 - undefined

-2
对我来说,通过重新启动解决了问题。
wsl --shutdown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接