Windows与Linux两种格式文件相互转化问题的简单解决方案
一. 问题描述
在「Linux下批量运行测试算例的简单实现」一文中介绍了批量运行测试算例的方案,在其中我提到了修改每次计算的输入参数时,如果参数没有什么规律,只能手动的去修改输入参数文件,然而对于大数量级的测试次数,如果真的要手动去改输入参数,那是非常低效的,在经过两轮的手动改参数后我真受不了了,即便输入参数没有多少规律,也应尽量让程序来代工,手动改就是机械的浪费时间没有任何意义,把手动的过程用代码来实现虽然开始可能会费点时间,但这是个先苦后甜的过程,再者,相比机械的手动改参数,写程序还能多少有点技术性的收获,岂不更好。
这次遇到的问题是写了个代工程序,叫做SubmitUpdater
,通过它来更新及生成大批量的Shell脚本文件,它们叫做Submit-1.sh
-Submit-N.sh
,这N份输入参数文件需要放到Linux服务器上进行计算。在Linux中执行.sh脚本的时候出现异常,具体表现为/bin/bash^M: bad interpreter: No such file or directory
。在此对这个问题的解决方案进行简单总结,以做备忘。
二. 具体表现
先看一眼Submit-1.sh
里面的内容,很简单的Shell脚本。
1 | #!/bin/bash |
这是执行Submit-1.sh
的情况
1 | hmliao@songdyn:~> ./Submit-1.sh |
很显然,是/bin/bash^M
这里多了个^M
导致在Linux下执行的时候无法识别命令,为什么会多出来一个^M
呢?这得从Windows系统和Linux系统的换行标识符来说明。
在Windows系统下的换行标识为\r\n
,而Linux格式的换行标识为\n
。其中\r
表示回车符,\n
表示新的一行new line。为何Linux下的换行符不包含\r
回车符呢?大概是因为在Linux下处理包含回车符的时候会引起一些程序出现问题,因此和Windows的格式不一致。显然Windows和Linux的格式不一致是众所周知的,然而没有在实际中真正遇到的时候,往往我们是不会意识到它们的差异。
回归正题,在Linux下,回车符显示为^M
,而新换一行则用$
表示,根据上面的分析,我们是在Windows环境下用SubmitUpdater
生成Submit-1.sh
-Submit-N.sh
N份Shell脚本,Windows会给它们的行末加上换行标识\r\n
,即便你在代码中明明写的是\n
。例如:
1 | #!/bin/bash |
因此实际上,上面的#!/bin/bash
末尾接了一个Windows下的换行标识\r\n
,那么在Linux下显示的时候应该就是#!/bin/bash^M$
,我们可以在Linux下通过cat -A filename
命令来查看一个脚本文件是Windows格式还是Linux格式。
1 | hmliao@songdyn:~> cat -A Submit-1.sh |
从上面可以看到#!/bin/bash^M$
这是Windows格式,显然不是Linux能够识别的#!/bin/bash$
,既然是格式不对,能想到的解决方法不外乎就是进行格式转换或者直接生成Linux格式的文件。
三. 解决方案
1. 格式转换
Google搜了搜格式转换的方法琳琅满目,很多都是这样的情况:用各种文本编辑器打开文件,然后转存为Linux格式。这当然是一种方法,然而对于我的问题并不适用,我有几千份Windows格式的文件,一份份去转存终究是不现实,如果再写个程序来批处理这样的行为显然就没有意义。
对于我这样的,已经有大批量Windows格式的文件的问题,一种可行的格式转换方法是通过Linux下的dos2unix
命令(当然了,同样也有unix2dos
),然后在Linux服务器上用dos2unix
命令写个循环的脚本文件,就可以轻松的完成格式转换。然而又出现一个问题了,我的Linux服务器上出现-bash: dos2unix: command not found
,如下:
1 | hmliao@songdyn:~> dos2unix Submit-1.sh |
估计是得安装一下.
update: 2017年12月18日15:33:39
对于 Linux 操作系统,Dos2unix 程序已经被添加到系统的软件源内,可以直接从软件源进行安装。Fedora、CentOS 等 Linux 发行版的安装命令为:
1 | sudo yum install dos2unix unix2dos |
然而我的账号并没有权限,由于我是通过cygwin/ssh的方式来连接Linux服务器,一个可行的办法是在cygwin模拟的Linux环境下安装dos2unix
,然后批量转格式,然后上传到服务器进行计算。
- 首先下载「apt-cyg」保存的文件名为apt-cyg,没有后缀,然后放到你的cygwin安装目录下的/bin目录里面,然后修改apt-cyg给执行权限,参考以下命令
1 | chmod +x /bin/apt-cyg |
- 然后运行cygwin的安装包,在选择包的页面安装wget,如图,接着等待安装完成
- 最后打开cygwin终端,执行
apt-cyg install dos2unix
进行安装。
此方法经测试有效,如下
1 | hxl650@BoLi-PC /home |
可以看到转换后的文件,没有了^M
1
2
3
4
5
6
7$ cat -A Submit-1.sh
hxl650@BoLi-PC /home
#!/bin/bash$
./$1 -2 -L 290 -H 120 -J 0.1 -h 8.0 -D 290 -r 1.5 -c 1.0e-5 -x 3.1 -b 1.6 -q 1 -R 1
-M 0.01 -U -Q -t 0.1 -T 0.05 -d 1000 -v 1513 -f 1 -G 1.0e7 -k 1.42e5 -p 1.18e-6
-g 1.4 -m 0 -s 1.82e-5 -i 0.04 -l 0.01 -I 0.0 -A 0.0 -E 2.5e6 -P 1e-4 -N 0.35 -n 8
-K 0.010000
再通过写个循环脚本就可以实现批量转换了,例如
1 | #!/bin/bash |
批量转换格式后上传到Linux服务器进行计算,则通过格式转换的途径解决了这个问题,但是这他妈的也实在是太繁琐了吧,简直不能忍,也就是一直不甘心一直想把它成功转换,我才坚持把这个方法做通,但是太繁琐了,不推荐。如果有兴趣倒不妨尝试一下。
2. 直接生成Linux格式的文件
前面说过了,不外乎转格式和直接生成正确格式两种方法。现在就简单说说直接生成Linux格式的文件的方法,前面说了半天那么详细的说,为什么到这里就简单说说了呢,那是因为这个方法太简单了。
第一,如果是用文本编辑器手动编辑的脚本文件,那在保存的时候注意一下保存的格式,比如说windows的文本编辑器notepad,在保存的时候,选择为Unix格式的,这样传到Linux服务器的时候格式就没有问题了。当然还有其他很多种文本编辑器了,一样的,就是保存格式设置一下。当然这种情况对我的需求并不适用,我有成百上千份文件,我不可能手动一份创建然后保存。所以需要写程序来做这些工作,也就是前面提到的SubmitUpdater
,用SubmitUpdater
来生成成百上千份的文件,似乎又回到了需要进行前面格式转换的问题了是不是?其实只要不要在Windows下编译SubmitUpdater
即可,把编译工作放到Linux服务器上去做,然后在Linux下执行SubmitUpdater
,直接生成Linux格式的脚本文件,这才是正道。不要纠结怎么进行格式转换了,虽然研究起来也蛮有意思。
第二,要把程序放到Linux上去编译,那得自己写makefile了,然而不写不知道,写了才知道makefile有多简单,下面是一个非常好的教程「跟我一起写Makefile:MakeFile介绍」,推荐学习,此处不再赘述了。
update: 2017-6-5 10:36:21
3. 在windows下生成用于linux计算用的文件时保证不输入\r即可
详见:How do I stop fprintf from printing \r’s to file along with \n in Windows1
2
3
4
5
6
7
8
9
10
11
12
13
14
15FILE *outputFile = NULL;
outputFile = fopen(outputFileName, "wb");
// Output element sets data
for (int i = 0; i < elementSets.size(); i++)
{
fprintf(outputFile, "8 %s %d\n",
elementSets[i].name,
elementSets[i].element.size());
for (int j = 0; j < elementSets[i].element.size(); j++)
{
fprintf(outputFile, "%d\n", elementSets[i].element[j]);
}
}
四. 参考资料
1. Converting files from Windows format to Unix format with dos2unix
2. cygwin $’\r’: command not found 解决 dos2unix
3. 跟我一起写Makefile:MakeFile介绍