Batch renaming files in OSX terminal - bash

I'm trying to rename files e.g. screen-0001.tif to 0001.tif using the approach in this SO question:
for file in *.tif
do
echo mv "$file" "${screen-/file}"
done
fails to change anything. Grateful for an idea where I'm going wrong.

The easier, IMHO, way to do this is using Perl's rename script. Here I am using it with --dry-run so it just tells you what it would do, rather than actually doing anything. You would just remove --dry-run if/when you are happy with the command:
rename --dry-run 's/screen-//' *tif
'screen-001.tif' would be renamed to '001.tif'
'screen-002.tif' would be renamed to '002.tif'
'screen-003.tif' would be renamed to '003.tif'
'screen-004.tif' would be renamed to '004.tif'
It has the added benefit that it will not overwrite any files that happen to come out to the same name. So, if you had files:
screen-001.tif
0screen-01.tif
And you did this, you would get:
rename 's/screen-//' *tif
'screen-001.tif' not renamed: '001.tif' already exists
rename is easily installed using Homebrew, using:
brew install rename

Two things:
You're echoing the commands and not actually executing them. I will do this when I do massive renames just to make sure that the command works correctly. I can redirect the output to a file, and then use that file as a shell script.
The substitution is wrong. There are two ways:
Left most filter ${file#screen-}.
Substitution: ${file/screen/}
The name of the environment variable always goes first. Then the pattern type, then the pattern
Here's how I would do this:
$ for file in *.tif
> do
> echo "mv '$file' '${file#screen-}'"
> done | tee mymove.sh # Build a shell script
$ vi mymove.sh # Examine the shell script and make sure everything is correct
$ bash mymove.sh # If all is good, execute the shell script.

Related

Shell Command For File To Delete Itself

I want to make a file that runs a script, then deletes itself. I know that its root would most likely be "~/Library/Downloads/filename.app". How would I go about having it self destruct? I'm working in script editor.
I'm not sure if I understand correctly as shell script would traditionally have .sh suffix instead of .app one (if any) and I'm not familiar with anything that I'd call "script editor", but alas here's my solution.
If you are in bash environment, you can make use of the BASH_SOURCE array. Provided that you didn't change the current working directory, you can directly call
rm "${BASH_SOURCE[0]}"
(or just rm "$BASH_SOURCE").
If you are using cd or make larger script, it might be advisable to save fully resolved path to the script at the beginning and remove that file at the end (not somewhere in the middle as running bash scripts are NOT independent on their source files*) like so:
#!/bin/bash
self=$(realpath "${BASH_SOURCE[0]}")
#
# code so ugly I want to delete it when I'm done
#
rm "$self"
*Edit shell script while it's running

Rename files using rename command in bash shell

I have read other similar questions on the forum and I can't understand why the command I tried doesn't work.
I have a list of files named in the form aaaa_100_aaaa.csv, aaaa_100_aaab.csv, aaaa_100_aaac.csv and so on, and I want to replace "100" with "200".
I'm running bash in Windows PowerShell WSL. I tried with this command
rename 's/420/410/g' *.csv
I found the same expression in many answers on the forum but it doesn't work. I got the error message
mv: target 'aaaa_100_aaaa.csv' is not a directory.
Given that the error message starts with mv:, and therefore apparently is produced by the mv ("move") command, I'm willing to bet that your bash has been configured to treat rename as an alternative name for mv. So you aren't really running the rename command at all.
To check this, run type rename. It will probably tell you that rename is an alias or a shell function, not the reference to the /usr/bin/rename executable that you expected.
You can get around this by using the full pathname to invoke rename:
/usr/bin/rename 's/100/200/g' *.csv
or by writing a backslash in front of rename to tell bash to skip any special handling of the command name:
\rename 's/100/200/g' *.csv
Of course if you're going to want to use the real rename often then remembering to do that every time will be annoying. You could unalias rename but that only fixes it in the current shell.
The long-term solution is to prevent bash from treating rename as a shortcut. To do that you'll first have to find out where the alias or function is being defined, and then remove that definition. It's probably in your $HOME/.bashrc or $HOME/.bash_profile file. If it's not there then something like grep rename $HOME/.* should find it. If that doesn't find it then it might be in a system startup file that you can't (or don't want to) edit, and in that case you could get rid of it by adding unalias rename to your .bashrc or .bash_profile.

How do I call rename successfully from a bash script on Ubuntu?

I have a bash script #!/usr/bin/env bash that is called part of a make process. This script creates a directory with the files pertinent to a realise and then tars them up. I would like to take a copy of the directory and rename some of the files to replace the version identifier with the word "latest". This will make it simple to script the acquisition of the latest file from a web-server. When I run my script, the call to rename seems to do nothing, why is that?
#!/usr/bin/env bash
DATE_NOW="$(date +'%Y%m%d')"
product_id_base="$1"
firmware_dir="${product_id_base}-full-${DATE_NOW}"
# ...rest of file ommitted to protest the innocent
# It creates and fills the ${firmware_dir} with some files that end in
# -$DATE_NOW.<extention> and I would like to rename the copies of them so that they end in
# -latest.<extention>
cp -a "./${firmware_dir}" "./${product_id_base}-full-latest"
# see what there is in pwd
cd "./${product_id_base}-full-latest"
list_output=`ls`
echo $list_output
# Things go OK until this point.
replacment="'s/${DATE_NOW}/latest/'"
rename_path=$(which rename)
echo $replacment
perl $rename_path -v $replacment *
echo $cmd
pwd
$cmd
echo "'s/-${DATE_NOW}/-latest/g'" "${product_id_base}-*"
echo $a
# check what has happened
list_output=`ls`
echo $list_output
I call the above with ./rename.sh product-id and get the expected output from ls that indicates the present working directory is the one full of files that I want renamed.
$ ./rename.sh product-id ET-PIC-v1.1.dat ET-PIC-v1.1.hex
product-id-20160321.bin product-id-20160321.dat
product-id-20160321.elf product-id-20160321.gz 's/20160321/latest/'
/home/thomasthorne/work/product-id/build/product-id-full-latest
's/-20160321/-latest/g' product-id-*
ET-PIC-v1.1.dat ET-PIC-v1.1.hex product-id-20160321.bin
product-id-20160321.dat product-id-20160321.elf product-id-20160321.gz
What I hopped to see was some renamed files. When I directly call the rename function from a terminal emulator I see the rename occur.
~/work/product-id/build/product-id-full-latest$ rename -vn
's/-20160321/-latest/g' * product-id-20160321.bin renamed as
product-id-latest.bin product-id-20160321.dat renamed as
product-id-latest.dat product-id-20160321.elf renamed as
product-id-latest.elf ...
I have tried a few variations on escaping the strings, using ` or $(), removing all the substitutions from the command line. So far nothing has worked so I must be missing something fundamental.
I have read that #!/usr/bin/env bash behaves much like #!/bin/bash so I don't think that is at play. I know that Ubuntu and Debian have different versions of the rename script to some other distributions and I am running on Ubuntu. That lead me to try calling perl /usr/bin/rename ... instead of just rename but that seems to have made no perceivable difference.
This string:
replacment="'s/${DATE_NOW}/latest/'"
will be kept exactly the same because you put it between single quotes.
Have you tried with:
replacment="s/${DATE_NOW}/latest/"
This one worked on my Ubuntu, without perl:
$ ./test_script
filename_20160321 renamed as filename_latest
filename2_20160321 renamed as filename2_latest
filename3_20160321 renamed as filename3_latest
test_script content being:
#!/bin/bash
DATE_NOW="$(date +'%Y%m%d')"
replacment="s/${DATE_NOW}/latest/"
rename -v $replacment *

How to rename file with bash by adding first symbol - dot?

I would like to rename my file from myscript.js to .myscript.js. How can I do it with bash? I've tried different options like mv myscript.js \.*, but it doesn't work. I've tried to experiment by adding non-dot symbol like - mv myscript.js m*, but now I don't even know where my file is (of course, I have a copy ;).
You're trying too hard.
mv myscript.js .myscript.js
Brace expansion
mv {,.}myscript.js
You can use rename utility to rename all *.js file by placing a dot before them:
rename 's/(.+\.js)/.$1/' *.js
Or in pure BASH use this for loop:
for i in *.js; do mv "$i" ".$i"; done
Don't use patterns as the target of mv (or cp for that matter). The command won't do what you want or expect, most of the time: Bash will expand the pattern at the moment when you run the command, using the file names as they were before your command was executed. So .* matches nothing (since the file doesn't exist, yet) and m* will match any file or folder which starts with m. Since you didn't get an error, chances are that the last match was a folder.
There is no way to access the previous parameter of the current command. If you want to avoid typing too much, then use Tab for both file names (so you end up with mv myscript.js myscript.js) and then use Ctrl+Left and Ctrl+Right to move quickly to the start of the second argument to insert the missing ..
You can access the parameters of the previous command, though. But that's not a feature of BASH - it's a feature of readline.

Rsync copies too many directories being executed via bash script

Originally I would like to sync directory (with all files and subdirectories) given in parameter in bash script.
I found this post: How can I recursively copy a directory into another and replace only the files that have not changed? which explains how to use rsync in similar case.
My bash script is quite simple and listed below:
#!/bin/bash
echo -e "Type the project to be deployed: \c "
read project
echo -e "* Deploying: $project *"
echo -e "Sync: /var/repo/released/$project"
echo -e " /var/www/released/$project"
rsync -pr /var/repo/released/$project /var/www/released/$project
As a result it copies everything within /released (there are many directories in there, let's say -projects-).
I would like to copy (sync) only project given in parameter.
Could you please advice how to do this?
When you call the script without an argument (which most likely is what you're doing since you interactively read the project name into the variable $project), the positional parameter $1 remains empty. Therefore the script will rsync the entire content of /var/repo/released/.
You need to replace $1 with $project in your script. Also, I'd recommend to put double quotes around the paths to avoid problems due to spaces in a directory name.
rsync -pr "/var/repo/released/$project" "/var/www/released/$project"

Resources