This question already has answers here:
Batch rename sequential files by padding with zeroes
(10 answers)
Closed 4 years ago.
I'm running into an issue where I have a directory of files names:
something1.exr
something2.exr
something3.exr
and I need them named like
projectname.0000001.exr
projectname.0000002.exr
projectname.0000003.exr
I've come up with this:
NAME=image_test_GAM_4778x1806 c=1; for i in *.exr; do mv "$i" `printf $NAME."$c".exr`; let c=c+1; done
and it is able to rename files like:
image_test_GAM_4778x1806.1.exr
image_test_GAM_4778x1806.2.exr
image_test_GAM_4778x1806.3.exr
However, I need the incremented number to have 7-digit padded zeros so I found I can do that with "%07d", therefore I assume this code should work:
NAME=image_test_GAM_4778x1806 c=1;
for i in *.exr; do
mv $i $(printf “%s.%07d.exr” “$NAME” “$c”);
let c=c+1;
done
But it doesn't work and complains I'm using mv incorrectly. I know theres something wrong but logically it should make work, I'm trying to pass $NAME and $c to “%s.%07d.exr” respectively.
Can someone point me in the right direction? Any help would be appreciated.
Thank you.
Using rename utility with a perl expression:
rename -n "s/something(\d+)(.*)/'projectname.' . sprintf('%07d', \$1) . \$2/ge" *.exr
Using -n flag is useful to test until you found the exact expression:
something10.exr renamed as projectname.0000010.exr
something1.exr renamed as projectname.0000001.exr
something2.exr renamed as projectname.0000002.exr
something3.exr renamed as projectname.0000003.exr
Thanks to Barmar's suggestion in the comments - this was an issue with curly quotes.
I changed the quotes to regular parallel quotes and it works.
Related
This question already has answers here:
Remove a fixed prefix/suffix from a string in Bash
(9 answers)
Closed last month.
I'm new to bash, and coding. I have a list of files:
test-T01___2022.txt
test-T01__2021.txt
test-T01_NONE.txt
test-T02___2022.txt
test-T02__2021.txt
test-T02_NONE.txt
test-T03___2022.txt
test-T03__2021.txt
test-T03_NONE.txt
I'm trying to write a script to create folders T01 (containing *T01 files), T02 (containing all files with T02), etc. I'm trying with wildcards and regexps and something similar to this post but having some trouble. I appreciate some help.
Many thanks!
Use the bash prefix and suffix removal operations. See the link in the comments for more details.
For example:
files=...
for file in $files
do
a=${file#test-}
dir=${a%%_*}
mkdir "$dir"
mv "$file" "$dir"
done
This question already has answers here:
remove date from filename but keep the file extension
(2 answers)
Closed 4 years ago.
Is there a quick and clever way to remove various timestamps from multiple files with different names? The timestamp format always remains the same, although the values differ. An example of my files would be...
A_BB_CC_20180424_134312
A_B_20180424_002243
AA_CC_DD_E_20180424_223422
C_DD_W_E_D_20180423_000001
with the expected output
A_BB_CC
A_B
AA_CC_DD_E
C_DD_W_E_D
Notice the last file has a different timestamp, I don't mind if this is a day specific timestamp removal or all, or two variations. My problem is I can't think of the code for an ever changing time value :(
Thanks in advance
EDIT - Adding edit in to show why this is not a duplicate as Tripleee thinks. His duplicate link is for files with the same prefix, my question is about files with different names so the answer is different.
Using parameter expansion %% bashism that removes the end of the filename:
for i in /my/path/*; do mv "$i" "${i%%_2018*}"; done
This relies on the timestamp that start with 2018...
Using awk:
for i in /my/path/*; do mv "$i" $(awk -v FS=_ 'NF-=2' OFS="_" <<< "$i"); done
This awk script is based on the field separator _. It prints the filename without the last 2 field representing the timestamp.
In order to rename a set of files and apply regular expressions in the renaming process you can use the rename command.
So in your example:
rename 's#_[0-9]*_[0-9]*##' *_[0-9]*
This renames all files in the current directory ending with _ followed by digits.
It cuts away all _ followed by digits followed by _ followed by digits.
This question already has answers here:
Remove a fixed prefix/suffix from a string in Bash
(9 answers)
Closed 6 years ago.
I learned how to remove prefix and suffix respectively as below:
p="prefix-foo-bar-suffix"
echo ${p#prefix} # -foo-bar-suffix
echo ${p%suffix} # prefix-foo-bar-
and I am trying to figure out how to remove them together based on the examples above. I tried the code below but it does not work.
echo ${p#prefix%suffix} # prefix-foo-bar-suffix, looks like it treats "prefix%suffix" as a whole thing
echo ${{p#prefix}%suffix} # error, bad substitution
P.S. I know it should be easy to make it work using regex, but here I want to know if it is possible to construct a solution that just builds on top of the # and % tricks. Also, using eval may make it very easy, but as some people suggest, I tend to avoid it here.
Introduce a helper function:
$ trim() { local x="${1#"$2"}"; echo "${x%"$3"}"; }
$ trim prefix-foo-bar-suffix prefix suffix
-foo-bar-
$ trim prefix-foo-bar-suffix prefix
-foo-bar-suffix
$ trim prefix-foo-bar-suffix "" suffix
prefix-foo-bar-
Thank you for a good question.
joint use a prefix and suffix is not possible, or do I just could not find it in the documentation.
but if you know the length of a suffix and a prefix that you may fit this simple solution:
echo ${p:6: $[ ${#p}-12 ]}
I am trying to make heads or tails of a shell script. Could someone please explain this line?
$FILEDIR is a directory containing files. F is a marker in an array of files that is returned from this command:
files=$( find $FILEDIR -type f | grep -v .rpmsave\$ | grep -v .swp\$ )
The confusing line is within a for loop.
for f in $files; do
target=${f:${#FILEDIR}}
<<do some more stuff>>
done
I've never seen the colon, and the hash before in a shell script for loop. I haven't been able to find any documentation on them... could someone try and enlighten me? I'd appreciate it.
There are no arrays involved here. POSIX sh doesn't have arrays (assuming you're not using another shell based upon the tags).
The colon indicates a Bash/Ksh substring expansion. These are also not POSIX. The # prefix expands to the number of characters in the parameter. I imagine they intended to chop off the directory part and assign it to target.
To explain the rest of that: first find is run and hilariously piped into two greps which do what could have been done with find alone (except breaking on possible filenames containing newlines), and the output saved into files. This is also something that can't really be done correctly if restricted only to POSIX tools, but there are better ways.
Next, files is expanded unquoted and mutalated by the shell in more ridiculous ways for the for loop to iterate over the meaningless results. If the rest of the script is this bad, probably throw it out and start over. There's no way that will do what's expected.
The colon can be as a substring. So:
A=abcdefg
echo ${A:4}
will print the output:
efg
I'm not sure why they would use a file directory as the 2nd parameter though...
If you are having problems understanding the for loop section, try http://www.dreamsyssoft.com/unix-shell-scripting/loop-tutorial.php
Here's a simple problem that's been bugging me for some time. I often find I have a number of input files in some directory, and I want to construct output file names by replacing beginning and ending portions. For example, given this:
source/foo.c
source/bar.c
source/foo_bar.c
I often end up writing BASH expressions like:
for f in source/*.c; do
a="obj/${f##*/}"
b="${a%.*}.obj"
process "$f" "$b"
done
to generate the commands
process "source/foo.c" "obj/foo.obj"
process "source/bar.c "obj/bar.obj"
process "source/foo_bar.c "obj/foo_bar.obj"
The above works, but its a lot wordier than I like, and I would prefer to avoid the temporary variables. Ideally there would be some command that could replace the beginning and ends of a string in one shot, so that I could just write something like:
for f in source/*.c; do process "$f" "obj/${f##*/%.*}.obj"; done
Of course, the above doesn't work. Does anyone know something that will? I'm just trying to save myself some typing here.
Not the prettiest thing in the world, but you can use a regular expression to group the content you want to pick out, and then refer to the BASH_REMATCH array:
if [[ $f =~ ^source/(.*).c$ ]] ; then f="obj/${BASH_REMATCH[1]}.o"; fi
you shouldn't have to worry about your code being "wordier" or not. In fact, being a bit verbose is no harm, consider how much it will improve your(or someone else) understanding of the script. Besides, for performance, using bash's internal string manipulation is much faster than calling external commands. Lastly, you are not going to retype your commands every time you use it right? So why worry that its "wordier" since these commands are already in your script?
Not directly in bash. You can use sed, of course:
b="$(sed 's|^source/(.*).c$|obj/$1.obj|' <<< "$f")"
Why not simply using cd to remove the "source/" part?
This way we can avoid the temporary variables a and b:
for f in $(cd source; printf "%s\n" *.c); do
echo process "source/${f}" "obj/${f%.*}.obj"
done