我们正在开发一个angularjs项目,该项目的编译输出包含许多文件扩展名,例如js,css,woff等。以及作为文件名一部分的单个动态哈希。
我正在研究简单的bash脚本,以搜索属于上述文件扩展名的文件,并将其移至某个文件夹,并删除哈希 搜索“。”的第一个实例。
请注意文件扩展名.woff和.css应该保留。
/src/main.1cc794c25c00388d81bb.js ==> /dst/main.js
/src/polyfills.eda7b2736c9951cdce19.js ==> /dst/polyfills.js
/src/runtime.a2aefc53e5f0bce023ee.js ==> /dst/runtime.js
/src/styles.8f19c7d2fbe05fc53dc4.css ==> /dst/styles.css
/src/1.620807da7415abaeeb47.js ==> /dst/1.js
/src/2.93e8bd3b179a0199a6a3.js ==> /dst/2.js
/src/some-webfont.fee66e712a8a08eef580.woff ==> /dst/some-webfont.woff
/src/Web_Bd.d2138591460eab575216.woff ==> /dst/Web_Bd.woff
邮政编码:
#!/bin/bash
echo Process web binary files!
echo Processing the name change for js files!!!!!!!!!!!!!
sfidx=0;
SFILES=./src/*.js #{js,css,voff}
DST=./dst/
for files in $SFILES
do
echo $(basename $files)
cp $files ${DST}"${files//.*}".js
sfidx=$((sfidx+1))
done
echo Number of target files detected in srcdir $sfidx!!!!!!!!!!
上面的代码有2个问题, 需要在公共位置的for循环中添加文件扩展名,而不是为每个扩展名运行。但是,此方法失败,不确定是否需要更改。
SFILES=./src/*.{js,css,voff}
cp: cannot stat `./src/*.{js,css,voff}': No such file or directory
第二,由于以下原因导致cp cmd失败,需要一些帮助以找出正确的语法。
cp $files ${DST}"${files//.*}".js
1.620807da7415abaeeb47.js
cp: cannot create regular file `./dst/./src/1.620807da7415abaeeb47.js.js': No such file or directory
答案 0 :(得分:2)
这是一个相对简单的命令:
find ./src -type f \( -name \*.js -o -name \*.css -o -name \*.woff \) -print0 |
while IFS= read -r -d $'\0' line; do
dest="./dst/$(echo $(basename $line) | sed -E 's/(\..{20}\.)(js|css|woff)/\.\2/g')"
echo Copying $line to $dest
cp $line $dest
done
答案 1 :(得分:1)
这是基于原始代码并且是Shellcheck干净的:
#!/bin/bash
shopt -s nullglob # Make globs that match nothing expand to nothing
echo 'Process web binary files!'
echo 'Processing the name change for js, css, and woff files!!!!!!!!!!!!!'
srcfiles=( src/*.{js,css,woff} )
destdir=dst
for srcpath in "${srcfiles[@]}" ; do
filename=${srcpath##*/}
printf '%s\n' "$filename"
nohash_base=${filename%.*.*} # Remove the hash and suffix from the end
suffix=${filename##*.} # Remove everything up to the final '.'
newfilename=$nohash_base.$suffix
cp -- "$srcpath" "$destdir/$newfilename"
done
echo "Number of target files detected in srcdir ${#srcfiles[*]}!!!!!!!!!"
该代码使用数组而不是字符串来保存文件列表,因为它更容易(而且通常更安全,因为它可以处理名称包含空格和其他特殊字符的文件)。有关在Bash中使用数组的信息,请参见Arrays [Bash Hackers Wiki]。
有关使用${var##pattern}
等来提取部分字符串的信息,请参见Removing part of a string (BashFAQ/100 (How do I do string manipulation in bash?))。
有关为何最好避免使用大写变量名(例如SFILES
)的说明,请参见Correct Bash and shell script variable capitalization。
shopt -s nullglob
可防止在glob模式不匹配时发生奇怪的事情。有关更多信息,请参见Why is nullglob not default?。
请参阅Bash Pitfalls #2 (cp $file $target),以了解为什么通常最好使用cp --
而不是普通的cp
(尽管在这种情况下没有必要(因为两个参数都不能以'-'开头))。
最好保持Bash代码Shellcheck干净。当在问题中的代码上运行时,它可以识别关键问题,并建议使用数组作为解决问题的方法。它还确定了其他一些潜在问题。
答案 2 :(得分:0)
您的问题是扩展的重点。这是我的解决方案:
#!/bin/bash
echo Process web binary files!
echo Processing the name change for js files!!!!!!!!!!!!!
sfidx=0;
SFILES=$(echo ./src/*.{js,css,voff})
DST=./dst/
for file in $SFILES
do
new=${file##*/} # basename
new="${DST}${new%\.*}.js" # escape \ the .
echo "copying $file to $new" # sanity check
cp $file "$new"
sfidx=$((sfidx+1))
done
echo Number of target files detected in srcdir $sfidx!!!!!!!!!!
在./src中有三个文件都被命名为“ gash”,我得到了:
Process web binary files!
Processing the name change for js files!!!!!!!!!!!!!
copying ./src/gash.js to ./dst/gash.js
copying ./src/gash.css to ./dst/gash.js
copying ./src/gash.voff to ./dst/gash.js
Number of target files detected in srcdir 3!!!!!!!!!!
(您也许可以使用eval
来解决这个问题,但这可能是一个安全问题)
new=${file##*/}
-删除以/结尾的左侧最长的字符串(删除前导目录名称,如basename
)。如果您想使用外部非外壳basename
程序,那么它将是new=$(basename $file)
。
${new%\.*}
-从.
开始删除最短的字符串(删除旧的文件扩展名)
答案 3 :(得分:0)
一种可行的方法是让find
命令生成一个shell脚本,然后执行它。
src=./src
dst=./dst
find "$src" \( -name \*.js -o -name \*.woff -o -name \*.css \) \
-printf 'p="%p"; f="%f"; cp "$p" "'"${dst}"'/${f%%%%.*}.${f##*.}"\n'
这将打印您要执行的shell命令。如果他们是你想要的 只需将输出通过管道传递到外壳:
find "$src" \( -name \*.js -o -name \*.woff -o -name \*.css \) \
-printf 'p="%p"; f="%f"; cp "$p" "'"${dst}"'/${f%%%%.*}.${f##*.}"\n'|bash
(或|bash -x
,如果您想查看发生了什么。)
如果您有名为./src/dir1/a.xyz.js
和./src/dir2/a.uvw.js
的文件,它们都将以./dst/a.js
结尾,第二个将覆盖第一个。为避免这种情况,您可能要使用cp -i
而不是cp
。
如果您完全确定路径名中绝不会包含空格或其他奇怪的字符,则可以使用较少的引号(对某些shell纯粹主义者而言是恐怖的)
find $src \( -name \*.js -o -name \*.woff -o -name \*.css \) \
-printf "p=%p; f=%f; cp \$p ${dst}/\${f%%%%.*}.\${f##*.}\\n"|bash
一些最后的评论:
%p
和%f
被-printf
扩展为已处理文件的完整路径名和基名。它们使我们能够避免使用basename
命令。不幸的是,文件扩展名没有这样的指令,因此我们必须在shell中使用大括号扩展来组成最终名称。
在-printf
参数中,我们必须使用%%
来写一个百分号字符。由于我们需要两个,所以必须有四个...
${f%%.*}
在shell中随着$f
的值扩展,并且从第一个点开始都删除了所有内容
${f##*.}
在shell中扩展为$f
的值,并将所有内容删除到最后一个点(即,扩展为文件扩展名)