Bash编程(Cygwin):非法字符^M

7

我遇到了一个字符问题。我认为这是dos和unix之间的转换问题。

我有一个变量,其中包含浮点值。当我使用echo命令打印它时,输出结果如下:

0.495959

但是,当我尝试使用bc命令(我不确定如何编写bc命令)对该值进行操作时。

echo $mean *1000 |bc

I get:

(standard_in) 1 : illegal character: ^M

我已经在我的.sh文件上使用了dos2unix命令。

我认为这是因为我的变量中有^M字符(不会被echo命令输出)。

我应该如何消除这个错误?


$mean 是如何获得的? - netcoder
1
使用ImageMagick的“identify”命令。但由于我在Windows上,可能会以DOS格式而不是Unix格式给出结果。是什么导致添加回车字符? - Frencoo
好的,但是你运行的命令是什么,以便 $mean 被填充?类似于 mean=\identify ...``... 我们需要完整的一行。 - netcoder
mean=identify -colorspace gray -format %[fx:mean] $jpg_frame1 平均值=identify -colorspace gray -format %[fx:mean] $jpg_frame1 - Frencoo
7个回答

14

我手头没有Cygwin,但在常规的Bash中,你可以使用tr -d命令来删除指定的字符,并且你可以使用$'...' 符号来指定命令行参数中的奇怪字符(它类似于一个普通的单引号字符串,除了它支持C / Java / Perl等类似的转义序列)。因此,下面这个命令:

echo "$mean" * 1000 | tr -d $'\r' | bc

会在从 echobc 的过程中去除回车符。

你可能实际上想要运行这个命令:

mean=$(echo "$mean" | tr -d $'\r')

这将修改$mean以去除其中的回车符,然后您就不必担心在稍后使用它的命令中会出现问题。

(不过,值得一提的是,也值得看看设置$mean的代码。毕竟,为什么$mean会有回车符呢?也许您可以修复它。)


将 ANSI-C 风格的字符串转换为整数,这实际上是最佳实践,我认为。这是唯一对我有效的解决方案! - Payne

3

以下内容可实现目的:

${mean/^M/}

你可以通过按Ctrl-V,然后按Ctrl-M获取 ^M。或者,您也可以使用以下方法:

${mean/$(printf "\r")/}

@ruakh的方法相比,此方法的好处在于只使用了bash内置功能。第一个方法会更快,因为第二个方法将在子shell内运行。

如果您只想 "unixize" $mean:

mean="${mean/^M/}"

编辑: 还有一种方法:

${mean/$'\r'/}


2
在cygwin中运行Windows程序有一个不好的副作用,就像你发现的那样 - 在cygwin bash变量中捕获Windows程序的输出也会捕获程序生成的CR。
明智地使用d2u可以避免这个问题 - 例如,
runtime="`mediainfo --Inform='Video;%Duration%' ${movie} | d2u`"
< p >(如果没有d2u,${runtime}将在末尾添加CR,这会导致当您将其提供给“bc”时看到的问题。)

(没有d2u,${runtime}会在末尾添加CR,这会导致将其提供给“bc”等程序时出现问题。)

1
也许你应该将脚本保存为UNIX格式,而不是DOS格式。

我已经使用了dos2unix命令进行转换。这是你的意思吗? - Frencoo
是的,没错。我不知道为什么使用dos2unix没有解决你的问题。在我看来,代码看起来很好。我尝试了一下,它运行得很好。但是,如果我将脚本保存在dos模式下,我会得到相同的错误:(standard_in) 1: illegal character: ^M。通常我不使用dos2unix,只是因为我更喜欢编辑器。为了复制你的代码,我使用了pspad。它是免费的,也许你可以试试。 - loscuropresagio

0

试试这个:

echo `echo $mean` *1000 |bc

如果echo确实没有打印它,那么它应该可以工作。

2
这是一个好想法,但它不会有帮助。如果 $mean 包含 ^M,并且 Bash 将 ^M 识别为单词分隔符(默认情况下不会),那么 echo $mean * 1000 将已经删除 ^M,因为 $mean 没有被引用。如果是 echo 添加了 ^M,那么显然不起作用(正如你所意识到的那样),如果 ^M 在变量中而 Bash 没有将其识别为单词分隔符,则内部的 echo 仍将打印 ^M,而外部的 echo 将重复它——没有任何收获。 - ruakh
@ruakh 你说得对,我没有仔细考虑! - Elias Dorneles

0

^M 是一个回车符,在Windows中与换行符(\n)一起使用来表示下一行。然而,在UNIX世界中并不是这样做的,所以bash不会将其视为特殊字符,从而破坏语法。你需要做的是使用其中的一种方法删除该字符。例如,dos2unix工具可以派上用场。


正如我所提到的,我已经在我的.sh文件上使用了dos2unix命令。这是你想要的吗?还是我需要以另一种方式使用它? - Frencoo

0

正如其他人所指出的,这是一个Windows换行符问题。有很多方法可以解决这个问题,但问题是为什么会发生这种情况。

我可以看到这种情况发生在几个地方:

  • 这是一个WINDOWS环境变量,在Cygwin启动时设置。有时这些变量末尾会有CRLF。你提到这是一个特定的问题,但你没有说明它是在哪里设置的。

  • 你使用了Windows文本编辑器(如Notepad或Winpad)编辑了这个文件。

永远不要使用文本编辑器来编辑程序。使用程序编辑器。如果你喜欢VI,请下载VIM,它可用于Windows并且附带在Cygwin(和所有其他基于Unix的平台)中。如果VIM不适合你,请尝试更具图形化界面的Notepad++。这两个编辑器都处理行尾问题,并且可以在Windows中创建带有Unix行尾的脚本或在Cygwin中创建带有Windows行尾的文件。


  • 如果您使用VIM,可以执行以下操作更改行尾并设置它们:

    • 在命令模式下键入:set ff?以查看当前文件的行尾。
    • 在命令模式下键入:set ff=unix以设置Unix的行尾。
    • 在命令模式下键入:set ff=dos以设置Windows的行尾。
  • 如果您使用Notepad++

    • 您可以进入编辑-->EOL转换菜单项,查看当前行尾设置(未突出显示的设置)并更改它。
    • 要使Notepad++默认使用Unix行尾,请进入设置-->首选项菜单项。在对话框中,选择新文档/默认目录选项卡。在新文档部分的格式部分中,选择所需的行尾。 警告:不要选择Mac作为选项。这甚至在Mac上都无法正常工作。如果您有Mac,请选择Unix

谢谢,我会尝试一下。顺便说一句,我之前在使用Wordpad,可能是导致问题的原因。 - Frencoo

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