What does an asterisk at the end of a mv command do - linux

So I am going along and moving a bunch of files
mv /source /dest &
mv /source/* /dest/dest/ &
...
...
then I get careless and
mv /source/filena* /dest/dest/ *
OMG! ^c^c^c^c
[No Response from terminal command]
What is actually going on here?
What happens when I put an * (asterisk) at the end of a command instead of an & (ampersand)?

The shell expands the wildcard *. The mv command never sees the wildcard, only the result of the expansion.
The wildcard * expands to the list of files in the current directory in lexicographic order. If the last file is a directory, then all the preceding files (/source.filenafoo, /source/filenabar, /dest/dest, hello) are moved to that subdirectory. If the last file is not a directory, mv complains that “target a.png is not a directory” (or words to that effect).
See What does mv ./* without specifying destination do? for more detailed examples.

An asterisk at the end of a command line is treated the same way as an asterisk anywhere else on the line — it's a wildcard that matches zero or more characters. Specifically, in this instance, the * in mv /source/filena* /dest/dest/ * is replaced by the name of each & every file and folder in your current directory (except those beginning with a dot), and whatever happens to be last in this list is where mv is going to try to put everything.

Related

How to copy multiple files with varying version numbers from one directory to another using bash?

I have a folder /home/user/Document/filepath where I have three files namely file1-1.1.0.txt, file2-1.1.1.txt, file3-1.1.2.txt
and another folder named /home/user/Document/backuppath where I have to move files from /home/user/Document/folderpath which has file1-1.0.0.txt, file2-1.0.1.txt and file3-1.0.2.txt
task is to copy the specific files from folder path to backup path.
To summarize:
the below is the files.txt where I listed the files which has to be copied:
file1-*.txt
file2-*.txt
The below is the move.sh script that execute the movements
for file in `cat files.txt`; do cp "/home/user/Document/folderpath/$file" "/home/user/Documents/backuppath/" ; done
for the above script I am getting the error like
cp: cannot stat '/home/user/Document/folderpath/file1-*.txt': No such file or directory found
cp: cannot stat '/home/user/Document/folderpath/file2-*.txt': No such file or directory found
what I would like to accomplish is that I would like to use the script to copy specific files using * in the place of version numbers., since the version number may vary in the future.
You have wildcard characters in your files.txt. In your cp command, you are using quotes. These quotes prevent the wildcards to be expanded, as you can clearly see from the error message.
One obvious possibility is to not use quotes:
cp /home/user/Document/folderpath/$file /home/user/Documents/backuppath/
Or not use a loop at all:
cp $(<files.txt) /home/user/Documents/backuppath/
However, this would of course break if one line in your files.txt is a filename pattern which contains white spaces. Therefore, I would recommend a second loop over the expanded pattern:
while read file # Puts the next line into 'file'
do
for f in $file # This expands the pattern in 'file'
do
cp "/home/user/Document/folderpath/$f" /home/user/Documents/backuppath
done
done < files.txt

Remove the last X files of a directory

In colab, I have unzip a file, but now there is too much files in the directory according to the colab's setup. Is there a command line to remove the last x files of a directory?
I know I can remove all the files from this repository with rm -rf *, but I just want to remove for instance the last 100 files of the repository.
Try globing or better REGEX.
The most easy way is with globing you use the star * and some differentiation example: rm *.txt # will delete all files that end with .txt or rm document*.local # will delete all files which start with document and end with .local
The better wey is searching files by attribut and executing command on the result but is a bit complex to explain so check this out.
https://www.cyberciti.biz/faq/linux-unix-how-to-find-and-remove-files/
Using a shell array and parameter expansion:
all_files=(*)
printf '%s\n' "${all_files[#]: -100}" | nl
#rm "${all_files[#]: -100}"
Uncomment the last line if it looks like the correct list of files to delete.
The space between the colon and the minus sign is required to disambiguate from another form of parameter expansion.
Ref: 3.5.3 Shell Parameter Expansion

How can I move a file and append a timestamp to its name via cron job?

I have a file CBD_DATA.zip present in a directory: /home/cbd_dev/CBD_DATA.zip. I want to move it to the directory /home/sundaram_srivastava/archives/ as CBD_DATA_{DateTimeStamp}.zip.
I have tried using a cron job:
* * * * * mv /home/cbd_dev/CBD_DATA.zip /home/sundaram_srivastava/archives > /home/sundaram_srivastava/archives/CBD_DATA_`date +\%d\%m\%y`.zip
The problem with the cron job above is that it moves the file as CBD_DATA.zip into another directory with the same name and then it creates another file CBD_DATA_110620.
Now, the file CBD_DATA_110620 is 0 KB. So, in the destination directory, I have two files, CBD_DATA.zip and CBD_DATA_110620, but I want just one and it should not be empty.
What should I change in my cron code?
First, I'd try and figure out the command on its own, without a cron job. What you're doing is something like this (with shortened directory paths for readability):
mv /foo/CBD_DATA.zip /foo/archives > /foo/archives/CBD_DATA_`date +%d%m%y`.zip
This moves the file and then creates a new empty file; there is no output from the mv command, and the redirection has nothing to redirect, so the file with the datestamp is empty.
The second argument for the mv command is the new location itself; if it is a directory, the filename stays the same, but if it is not a directory, it is interpreted as the new name. You don't need any redirection.
mv /foo/CBD_DATA.zip "/foo/archives/CBD_DATA_$(date '+%d%m%y').zip"
I have replaced the deprecated backticks in the command substitution with $(...) and quoted the expansion. Side note: if you can choose the datestamp format, I'd strongly recommend using +%F (or %Y%m%d) instead so it sorts chronologically.
With your paths and escaped for a cron job (do you really want to run this every minute?):
* * * * * mv /home/cbd_dev/CBD_DATA.zip "/home/sundaram_srivastava/archives/CBD_DATA_$(date +\%d\%m\%y).zip"

Linux command and single quote

I thought the single quotes simply reserve the literal value of each character within the quotes and do not interfere with the command result except for those character-escaping situations.
For example, the two commands below returned the same result.
$ ls /home/testuser/tmp1
$ ls '/home/testuser/tmp1'
This command below ran successfully.
$ cp /home/testuser/tmp1/* /home/testuser/tmp2
However, the command below ran into error.
$ cp '/home/testuser/tmp1/*' /home/testuser/tmp2
cp: cannot stat '/home/testuser/tmp1/*': No such file or directory
What did I do wrong here?
* is what you call a character escaping situation. Since you preserved the literal value of *, it lost its meaning of "all files", and cp instead tries to copy a file named literally *.
When you run:
cp /home/testuser/tmp1/* /home/testuser/tmp2
The shell will transparently rewrite it into:
cp /home/testuser/tmp1/bar /home/testuser/tmp1/foo /home/testuser/tmp2
This process is known as "pathname expansion" or "globbing". When you quote the *, this rewrite does not happen.

linux batch rename directories and strip # character from name

i have a directory with a lot of subdirectories with a # infront of them:
#adhasdk
#ad18237
I want to rename them all and remove the # caracter
I tried to do:
rename -n `s/#//g` *
but didn't seem to work.
-bash: s/#//g: No such file or directory
Any ideas on this.
Thanks
Just use
$ rename 's/^#//' *
use -n just to check that what you think it would happen really happens.
In you example you have the clue about the wrong quotes used (backticks) in the error message
-bash: s/#//g: No such file or directory
bash is trying to execute a command named s/#//g.
No that using g (global) and not anchoring the regular expression you will replace any #, not just the one in the first position.
I don't know whether it's just a typo when you typed it here, but that "rename" command should work if:
you leave off the "-n" and
you quote the substitution with regular single-quotes and not back-quotes
The "-n" tells it to not really do anything. The back-quotes are just wrong (they mean something but not what you want here).
The problem is that you use backticks (`). You should use normal quotes:
rename -n 's/#//g' *
for DIR in \#*/
do
echo mv "$DIR" "${DIR/#\#/}"
done
I had to rename all folders inside a given folder. Each folder name had some text inside round braces. The following command removed the round braces from all folder names:
rename 's/(.+)//' *
Some distros doesn't support regexp in rename. You have to install prename. Even more, sometimes you can't install prename and you have to install gprename to have binary prename.
If you have 'prename' then just change backtick character " ` " to single quote and everything should work.
So the solution should be:
prename -n 's/#//g' *
or
prename -n 'y/#//' *

Resources