Rename files using rename command in bash shell - bash

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.

Related

How to execute a script within a subfolder of a directory in path

Consider the following folder structure:
$ tree ~/test_path
test_path
`-- sub_folder
`-- script.sh
1 directory, 1 file
Let's say that you have added test_path to your path by
export PATH=$PATH:~/test_path
$ whereis sub_folder
sub_folder: /home/murtraja/test_path/sub_folder
Now how to execute script.sh by calling sub_folder/script.sh?
$ sub_folder/script.sh
bash: sub_folder/script.sh: No such file or directory
EDIT: I don't want to change the call sub_folder/script.sh because this is called by another script which I cannot (am avoiding to) change.
Short answer: You can't, but depending on the set of constraints you're facing, there might be another way to handle it.
Long answer: When a command name contains at least one slash character, it treated as a path to the executable (i.e. it doesn't search the directories in $PATH). With the command name sub_folder/script.sh, it contains a slash, but doesn't start with a slash, so it'll be resolved relative to the current working directory.
So there are a couple of possibilities for making this work:
If you can cd to ~/test_path before running this, it'll find it directly. Of course, this may break other things (i.e. anything else that uses relative paths and/or plain filenames and expects them to be resolved somewhere else). Also, be sure to check for errors when you cd, or the script could execute in an unexpected directory, with unexpected consequences.
If the script needs to execute from a different working directory, you might be able to create a symbolic link from sub_folder in that working directory to ~/test_path/sub_folder. But depending on where the script's working directory is, this may be impossible or unsafe. I'd avoid using this option if possible.
There's also an option that depends on a weird/nonstandard feature of bash: the ability to define function names with slash in them. But this has weird limitations depending on the version of bash you have:
You can define a function like this:
sub_folder/script.sh() { ~/test_path/sub_folder/script.sh "$#"; }
and then either use export -f sub_folder/script.sh (so bash subprocesses inherit it), or do this in a wrapper script and then source the script you can't change from there (so it's the same shell, and inheritance isn't necessary).
Difficulty: some versions of bash refuse to export functions with weird names, and some refuse to inherit them. So the export method might or might not work (and might break unexpectedly due to an update). The source method might be better, but also might cause other trouble.
If there's any way at all to change the other script, that'd really be the best option.
Since you've added it to your path, you can just call the script by name. It should also let you tab complete as well.
$ ./script.sh

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.

bash script doesn't find mkdir [duplicate]

This question already has answers here:
Getting "command not found" error in bash script
(6 answers)
Closed 2 years ago.
I've created a simple script to check if a folder exists and if not to create it. The script that follow
#!/bin/bash
PATH=~/Dropbox/Web_Development/
FOLDER=Test
if [ ! -d $PATH$FOLDER ]
then
echo $PATH$FOLDER 'not exists'
/bin/mkdir $PATH$FOLDER
echo $PATH$FOLDER 'has been created'
fi
works only if the mkdir command is preceded by /bin/. Failing in that, bash env output the error message "command cannot be found".
I though this could have been related to the system $PATH variable, but it looks regular (to me) and the output is as following:
/Library/Frameworks/Python.framework/Versions/2.7/bin:/bin:/usr/local/bin:/usr/bin:/sbin:/usr/local/sbin:/usr/sbin
I'm not sure whether the order with the different bin folders have been listed make any difference, but the /bin one (where the mkdir on my OSX Maverick) seems to reside is there hence I would expect bash to being able to execute this.
In fact, if I call the bash command from terminal, by typing just mkdir bash output the help string to suggest me how the mkdir command should be used. This suggests me that at a first instance bash is able to recognise the $PATH variable.
So what could be the cause? Is there any relation between the opening statement at the top of my .sh - #!/bin/bash - file and the "default" folder?
Thanks
Yeah, sometimes it is a bad idea to use capital letters for constant variables, because there are some default ones using the same convention. You can see some of the default variables here (Scroll to Special Parameters and Variables section). So it is better to use long names if you don't want to get any clashes.
Another thing to note is that you're trying to replicate mkdir -p functionality, which creates a folder if it does not exist (also it does create all of the parents, which is what you need in most cases)
One more thing - you always have to quote variables, otherwise they get expanded. This may lead to some serious problems. Imagine that
fileToRemove='*'
rm $fileToRemove
This code will remove all files in the current folder, not a file named * as you might expect.
One more thing, you should separate path from a folder with /. Like this "$MY_PATH/$MY_FOLDER". That should be done in case you forget to include / character in your path variable. It does not hurt to have two slashes, that means that /home/////////user/// folder is exactly the same /home/user/ folder.
Sometimes it is tricky to get ~ working, so using $HOME is a bit safer and more readable anyway.
So here is your modified script:
#!/bin/bash
MY_PATH="$HOME/Dropbox/Web_Development/"
MY_FOLDER='Test'
mkdir -p "$MY_PATH/$MY_FOLDER"
The problem is that your script sets PATH to a single directory, and that single directory does not contain a program called mkdir.
Do not use PATH as the name of a variable (use it to list the directories to be searched for commands).
Do learn the list of standard environment variable names and those specific to the shell you use (e.g. bash shell variables). Or use a simple heuristic: reserved names are in upper-case, so use lower-case names for variables local to a script. (Most environment variables are in upper-case — standard or not standard.)
And you can simply ensure that the directory exists by using:
mkdir -p ~/Dropbox/Web_Development
If it already exists, no harm is done. If it does not exist, it is created, and any other directories needed on the path to the directory (eg ~/Dropbox) is also created if that is missing.

Why does this script work in the current directory but fail when placed in the path?

I wish to replace my failing memory with a very small shell script.
#!/bin/sh
if ! [ –a $1.sav ]; then
mv $1 $1.sav
cp $1.sav $1
fi
nano $1
is intended to save the original version of a script. If the original has been preserved before, it skips the move-and-copy-back (and I use move-and-copy-back to preserve the original timestamp).
This works as intended if, after I make it executable with chmod I launch it from within the directory where I am editing, e.g. with
./safe.sh filename
However, when I move it into /usr/bin and then I try to run it in a different directory (without the leading ./) it fails with:
*-bash: /usr/bin/safe.sh: /bin/sh: bad interpreter: Text file busy*
My question is, when I move this script into the path (verified by echo $PATH) why does it then fail?
D'oh? Inquiring minds want to know how to make this work.
The . command is not normally used to run standalone scripts, and that seems to be what is confusing you. . is more typically used interactively to add new bindings to your environment (e.g. defining shell functions). It is also used to similar effect within scripts (e.g. to load a script "library").
Once you mark the script executable (per the comments on your question), you should be able to run it equally well from the current directory (e.g. ./safe.sh filename) or from wherever it is in the path (e.g. safe.sh filename).
You may want to remove .sh from the name, to fit with the usual conventions of command names.
BTW: I note that you mistakenly capitalize If in the script.
The error bad interpreter: Text file busy occurs if the script is open for write (see this SE question and this SF question). Make sure you don't have it open (e.g. in a editor) when attempting to run it.

Batch renaming files in OSX terminal

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.

Resources