How can move the file and change the file extension with simple bash?

60 Views Asked by At

Select file which contains sqlite in my current directory:

ls | grep sqlite
null and empty string in sqlite.txt
pythoin write None into sqlite.txt

Move them into /tmp directory:

ls |grep sqlite |xargs -i mv {}  /tmp
ls /tmp | grep sqlite
null and empty string in sqlite.txt
pythoin write None into sqlite.txt

How can move them into /tmp and change the file extension as rst at the same time?

ls |grep sqlite | some_simple_bash_command

After executing it:

ls /tmp | grep sqlite
null and empty string in sqlite.rst
pythoin  write None   into sqlite.rst     

@David C. Rankin,grep sqlite * can't work at all!

enter image description here

2

There are 2 best solutions below

0
pjh On BEST ANSWER

Try this Shellcheck-clean code:

for f in *sqlite*; do
    mv -v -- "$f" "/tmp/${f%.*}.rst"
done
3
David C. Rankin On

Let's expand on the comment a bit. First, learn not to pipe ls to the utility you want to use. All utilities that operate on files will accept a list of filenames through file-globbing. So to grep all files in the present working directory containing "sqlite" it's just:

$ grep 'sqlite' *

To change a filename extension POSIX provides a parameter expansion with substring removal that allows you to trim from the right, e.g. ${var%pattern}, to trim to the last occurrence of pattern beginning from the right it is ${var%%pattern}. Note: pattern can contain normal file-globbing such as ? or *.

To trim from the left it is ${var#pattern} and ${var##pattern}, respectively. These are POSIX expansions meaning they are available in all POSIX shells. Bash on the other hand provides many, many more expansions (stuff between ${...}) that only work in bash (referred to as "bashisms") Use POSIX when available to solve your problem for maximum portability.

To change the extension of a filename stored in the variable "$f", it would be:

mv "$f" "${f%.old}.new"`

Where old and new are the old and new file extensions.

When you want to rename or move and rename all files that result from a glob selection (e.g. using pattern* or the like, a for loop is what you need, e.g to select all files in the current directory whose name begins with pattern you can use pattern* in

for f in pattern*; do
  printf "%s\n" "$f"
done

(which simply prints the filename)

To move all files beginning with pattern to the /tmp directory, it would be:

for f in pattern*; do
  mv "$f" /tmp
done

(note: xargs would do just fine here)

When you want to process files that are the result of running some other process or utility, bash provides process substitution allowing you to feed a while loop in the form of while read -r var; do #stuff with "$var"; done < <(process). In your example of grep sqlite to move all files returned to /tmp while changing the extension from old to new, that would be:

while read -r f; do
  mv "$f" "/tmp/${f%.old}.new"
done < <(grep 'sqlite' *)

(for POSIX shells you would pipe the result of grep to your while loop, e.g. grep 'sqlite' * | while read -r f ...)

Look things over and let me know if you have questions or if I missed the intent of your question.