循环筛选查找并执行操作

Looping over filtered find and performing an operation

我有一堆 Wordpress 文件的垃圾转储,我正在尝试将它们全部转换为 Markdown。

我写的脚本是:

htmlDocs=($(find . -print | grep -i '.*[.]html'))
for html in "${htmlDocs[@]}"
            do
                P_MD=${html}.markdown
                echo "${html} \> ${P_MD}"
                pandoc --ignore-args -r html -w markdown < "${html}" | awk 'NR > 130' | sed '/<div class="site-info">/,$d'  > "${P_MD}"                
            done

据我了解,第一行应该是在所有子目录中创建所有 html 文件的数组,然后 for 循环有一行创建一个具有 Markdown 名称的变量(后跟一个debugging echo),然后实际的 pandoc 命令进行转换。

一次一个,这个命令有效。

然而,当我尝试执行它时,OSX 给我:

$ ./pandoc_convert.command 
./pandoc_convert.command: line 1: : No such file or directory
./pandoc_convert.command: line 1: : No such file or directory

o_0

帮忙?

尝试添加 bash shebang 并设置 IFS 以处理文件夹和文件名中的空格:

#!/bin/bash
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")

htmlDocs=($(find . -print | grep -i '.*[.]html'))
for html in "${htmlDocs[@]}"
do
                P_MD=${html}.markdown
                echo "${html} \> ${P_MD}"
                pandoc --ignore-args -r html -w markdown < "${html}" | awk 'NR > 130' | sed '/<div class="site-info">/,$d'  > "${P_MD}" 
done
IFS=$SAVEIFS

脚本失败的原因可能有很多,因为你创建数组的方式不正确:

htmlDocs=($(find . -print | grep -i '.*[.]html'))

数组赋值形式为:NAME=(VALUE1 VALUE2 ... ),其中NAME为变量名,VALUE1VALUE2,其余为字段分隔$IFS(输入字段分隔符)变量中存在的字符。假设您找到一个带空格的文件名。然后表达式将在数组中创建单独的项目。

另一个问题是表达式不处理通配符,即基于 shell 扩展特殊字符的文件名生成,例如 *:

mkdir dir.html
touch \ *.html
touch a\ b\ c.html

a=($(find . -print | grep -i '.*[.]html'))
for html in "${a[@]}"; do echo ">>>${html}<<<"; done

输出

>>>./a<<<
>>>b<<<
>>>c.html<<<
>>>./<<<
>>>a b c.html<<<
>>>dir.html<<<
>>> *.html<<<
>>>./dir.html<<<

我知道有两种方法可以解决此问题:1) 暂时禁用 globbing,以及 2) 使用 mapfile 命令。

禁用 Globbing

# Disable globbing, remember current -f flag value
[[ "$-" == *f* ]] || globbing_disabled=1
set -f

IFS=$'\n' a=($(find . -print | grep -i '.*[.]html'))
for html in "${a[@]}"; do echo ">>>${html}<<<"; done

# Restore globbing
test -n "$globbing_disabled" && set +f

输出

>>>./  .html<<<
>>>./a b c.html<<<
>>>./ *.html<<<
>>>./dir.html<<<

使用mapfile

在Bash中引入了mapfile 4. 该命令从标准输入中读取行到一个索引数组中:

mapfile -t a < <(find . -print | grep -i '.*[.]html')
for html in "${a[@]}"; do echo ">>>${html}<<<"; done

find 选项

find命令选择所有类型的节点,包括目录。您应该使用 -type 选项,例如-type f 文件。

如果要使用正则表达式过滤结果集,请使用 -regex 选项,或 -iregex 进行不区分大小写的匹配:

mapfile -t a < <(find . -type f -iregex .*\.html$)
for html in "${a[@]}"; do echo ">>>${html}<<<"; done

输出

>>>./  .html<<<
>>>./a b c.html<<<
>>>./ *.html<<<

echo 对比 printf

最后,don't use echo in new software。使用 printf 代替:

mapfile -t a < <(find . -type f -iregex .*\.html$)
for html in "${a[@]}"; do printf '>>>%s<<<\n' "$html"; done

替代方法

但是,我宁愿使用 read:

管道循环
find . -type f -iregex .*\.html$ | while read line
do
  printf '>>>%s<<<\n' "$line"
done

在此示例中,read 命令从标准输入读取一行并将该值存储到 line 变量中。

虽然我喜欢 mapfile 功能,但我发现带有管道的代码更清晰。