最近遇到如下需求:
有一个文件,文件格式大概是这样
<filename1>\t<download_url1>
<filename2>\t<download_url2>编写一个脚本循环读取该文件的每一行,使用
axel
命令将download_url
下载下来,保存为本地文件filename
Bash
先给出Code:
|
下面对code逐一解释
#! /bin/bash
#!/bin/bash是指此脚本使用/bin/bash来解释执行,Bash脚本首行固定写法。
Bash注释
Bash中使用#
进行单行注释,见如上代码第三行
Bash 接收外部参数
我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……
特殊的 ,$0 表示脚本名称(含路径)。
另外,$# 表示传递参数的个数。
上面代码中cat $1 |
就是将第一个参数作为文件名,读取文件中的内容,通过管道命令|
将文件内容传给while循环
Bash 循环读取每一行
最简单的
while read line |
还可以使用for循环等,详细可参见参考资料1。
Bash按指定字符分割字符串
接上文,line是待处理的字符串,则指定分隔符\t
将line分割后的字符串数组存放到array,
array=(${line// / }) |
注意这里//
和/
之间是一个制表符,而不是空格。
我也曾使用array=(${line//\t/ })
来进行分割,但是测试后发现它会按照字符t
对字符串进行分割。
还没弄明白语法规则
Bash字符串拼接
cmd='axel -o '${array[0]}' "'${array[1]}'"' |
这一行就是取array中的值进行字符串拼接,注意取值的语法${}
Bash中对字符串拼接不需要使用
+
进行连接这里使用单引号是为了避免对双引号转义
Bash执行cmd
eval $cmd
是读取变量cmd的值当作一条Shell命令进行执行
改进
上面的代码先读取每一行,在对行按照制表符进行分割。其实可以更简洁:
while read filename url |
同样的需要注意filename
,url
之间是一个制表符而不是空格。
问题
当url过长时,该脚本并不能完整的读取url。例如,当文件如下时
covid19-image-dataset-collection-volumes-folder.zip https://storage.googleapis.com/kaggle-data-sets/862993/1470970/bundle/archive.zip?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=gcp-kaggle-com%40kaggle-161607.iam.gserviceaccount.com%2F20211221%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20211221T052609Z&X-Goog-Expires=259199&X-Goog-SignedHeaders=host&X-Goog-Signature=6549111df89ca0440058e7061ba9a13d72ea22ed7bcfdbcfc56ddadaf7a54ec4d3285487b269808d60cf0568264db1803899989475d8ca0aeec0777891f54df612a6a8f7ba808c8df3899feda1a94ca0507a580e407c0e42371d97e3ce1c75d598bed1f693bf841f7d4660294488e99fc944d6e1498c5ec38b431b969e5f13eda5f2d8029e855ea368c57eab60897a32f7556df116a96bfd321dd4d214dfe847d4c8ca11fdb7c3472ebac9f7a9eb20b44a148df51ecb2f93acdc5459800a894cdc2d62a0cc2c2e42e6711e568827f44b82d6faaa02aa1357c8d93140eb2c72bde9b8c1716e565800ac3ff812df304ce7a54c2a83896696ac5fa935329afa66ef |
读取并拼接后的命令字符串如:
axel -o covid19-image-dataset-collection-volumes-folder.zip "https://storage.googleapis.com/kaggle-data-sets/862993/1470970/bundle/archive.zip?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=gcp-kaggle-com%40kaggle-161607.iam.gserviceaccount.com%2F20211221%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20211221T052609Z&X-Goog-Expires=259199&X-Goog-SignedHeaders=host&X-Goog-Signature=6549111df89ca0440058e7061ba9a13d72ea22ed7bcfdbcfc56ddadaf7a54ec4d3285487b269808d60cf0568264db1803899989475d8ca0aeec0777891f54df612a6a8f7ba808c8df3899feda1a94ca0507a580e407c0e42371d97e3ce1c75d598bed1f693bf841f7d4660294488e99fc944d6e1498c5ec38b431b969e5f13eda5f2d8029e855ea368c57eab60897a32f7556df116a96bfd321dd4d214dfe847d4c8ca11fdb7c3472ebac9f7a9eb20b44a148df51ecb2f93acdc5459800a894cdc2d62a0cc2c2e42e6711e568827f44b82d6faaa02aa1357c8d93140eb2c72bde9b8c1716e565800ac3ff812df304ce7a54c2a83896696ac5fa9353"9afa66ef |
注意引号外面还有数据,这是不符合预期的。
目前还没有解决该问题
Python
由于本人对Bash不是很熟悉,所以改用Python来完成这一需求。
下面给出完整代码
import argparse |
Python main函数
if __name__ == '__main__': |
这是Python的main函数,Python脚本总会从该“函数“进入。
Python 接收外部参数
使用类库argparse
来读取外部参数。上述代码给出了一个简单的示例
parser = argparse.ArgumentParser(description="download kaggle dataset according to file") |
详细请参见参考资料2
Python文件、字符串操作
略
在Python中执行Shell命令
有两种方式。使用os.system("command")
执行无返回值的Shell命令;使用f = os.popen("command")
执行有输出的Shell命令,其返回值f
是一个文件对象,通过f.read()
来读取命令输出内容。