Do not show results if directory is empty using Bash - bash

For example, try following command in an empty directory:
$ for i in *; do echo $i; done
*
Is there a way to suppress the printout of *?

Set nullglob
shopt -s nullglob
for i in *; do echo "$i"; done

Basic idea is use ls command, but if filename has space, ls will split file name by space. In order to handle space in filename, you can do like this:
ls|while read i; do echo $i; done
Aleks-Daniel Jakimenko said "Do not parse ls". Which is good, so how about this if we don't want to change nullglob:
for i in *; do [ -e "$i" ] && echo $i; done

To avoid the *, you can try something like
for i in `ls`; do echo $i; done
Tried now on an empty directory, no output given...

Related

inputting multiple arguments into gzip to gzip select files? [duplicate]

I want to excluse a specific filename (say, fubar.log) from a shell (bash) globbing string, *.log. Nothing of what I tried seems to work, because globbing doesn't use the standard RE set.
Test case : the directory contains
fubar.log
fubaz.log
barbaz.log
text.txt
and only fubaz.log barbaz.log must be expanded by the glob.
if you are using bash
#!/bin/bash
shopt -s extglob
ls !(fubar).log
or without extglob
shopt -u extglob
for file in !(fubar).log
do
echo "$file"
done
or
for file in *log
do
case "$file" in
fubar* ) continue;;
* ) echo "do your stuff with $file";;
esac
done
Why don't you use grep? For example:
ls |grep -v fubar|while read line; do echo "reading $line"; done;
And here is the output:
reading barbaz.log
reading fubaz.log
reading text.txt

Bash Script - creating folders and moving the files to it (specific number of files in each folder)

I would like to seek help regarding the code of creating folders and moving files to it. I want to create folders, place the files ending with specific extension and only certain number of files in each folder. For example, I have 10 text files, create 5 folders, each folder having two files. (Files have names like 1.txt, 2.txt , .... , 10.txt. So 1.txt and 2.txt should be in folder 1, 3.txt and 4.txt in folder 2 and so on.
My code looks like this:
end=2
sta=1
for i in {1..5}
do
mkdir "$i"
for file in *.txt:
do
mv "{$sta..$end}.txt" "$i"
done
end=$((end+2))
begin=$((begin+2))
done
It should be similar to it but I have an error,"mv: cannot stat '{1..10}.txt': No such file or directory".
I know it will be a simple change but couldn't figure it out. I have gone through the previous questions but couldn't sort my code.
Thanks!
As pointed here,
bash does brace expansion before variable expansion, so you get
weekly.{0..4}. Because the result is predictable and safe(Don't trust
user input), you can use eval in your case:
$ WEEKS_TO_SAVE=4
$ eval "mkdir -p weekly.{0..$((WEEKS_TO_SAVE))}"
note:
eval is evil use eval carefully Here, $((..)) is used to force the
variable to be evaluated as an integer expression.
So lets use eval in your case:
end=2
sta=1
for i in {1..5}
do
mkdir -p "$i"
for file in *.txt:
do
eval "mv {$((sta))..$((end))}.txt $i"
done
end=$((end+2))
sta=$((sta+2))
done
try it:
#!/bin/bash
sta=1
end=2
countFileInFolder=2
countFolders=5
filePath='/tmp/txt'
scriptDir=$(dirname $(readlink -f ${BASH_SOURCE}))
# create files for test
for (( k=1; k<=$(($countFolders*$countFileInFolder )); k++ )); do
> "$filePath/$k.txt"
done
for (( i=1; i<=$countFolders; i++ )); do
if [[ ! -d "$i" ]]; then mkdir "$i"; fi
for (( n=$sta; n<=$end; n++ )); do
if [[ ! -f "$i" ]]; then mv "$filePath/$n.txt" "$scriptDir/$i"; fi
done
sta=$(($sta+$countFileInFolder))
end=$(($end+$countFileInFolder))
done
You can try the arithmetic substitution version of for as below :
((for i=1; i<=5; i++))
Explanation : brace expansion, {x..y} is performed before other expansions, so you cannot use that for variable length sequences.
EDIT AFTER MV ERROR WAS RESOLVED
#!/bin/bash
count=$(ls -lrt *.txt | wc -l)
for ((i=1;i<=$count/2;i++))
do
mkdir $i
a=$(find . -maxdepth 1 -name '*.txt' | cut -d'/' -f2 | sort -n|head -2)
mv $a $i
done

bash How to escape specialcharacter '#' in filename template?

How to escape specialcharacter # in search filename template?
this code doesn't work (empty result)
for file in *_#2.png; do echo "$file"; done
also tried this:
for i in *_#2.png; do mv "$i" "${i/_#2.png}"#2x.png; done
I need to rename files *_#2.png to *#2x.png
Check the file exists
for file in *_#2.png; do
[[ -e $file ]] || continue # to skip file which not exists
...
done
Using shell options.
If failglob option is not set when not glob match the glob *_#2.png expand to itself
shopt globfail # to check value
shopt -s globfail # to turn on
shopt -u globfail # to turn off
solved
for i in *.png; do mv "$i" "${i/_#2.png/#2x.png}" ; done

Exclude specific filename from shell globbing

I want to excluse a specific filename (say, fubar.log) from a shell (bash) globbing string, *.log. Nothing of what I tried seems to work, because globbing doesn't use the standard RE set.
Test case : the directory contains
fubar.log
fubaz.log
barbaz.log
text.txt
and only fubaz.log barbaz.log must be expanded by the glob.
if you are using bash
#!/bin/bash
shopt -s extglob
ls !(fubar).log
or without extglob
shopt -u extglob
for file in !(fubar).log
do
echo "$file"
done
or
for file in *log
do
case "$file" in
fubar* ) continue;;
* ) echo "do your stuff with $file";;
esac
done
Why don't you use grep? For example:
ls |grep -v fubar|while read line; do echo "reading $line"; done;
And here is the output:
reading barbaz.log
reading fubaz.log
reading text.txt

Using $# properly

I am trying to write a tiny script that accepts any number of command line arguments that prints out the rwx permissions for a file (not directory)
What I have is
file=$#
if [ -f $file ] ; then
ls -l $file
fi
This accepts only one command line argument however. Thanks for any help.
Here is a demonstration of the some of the differences between $* and $#, with and without quotes:
#/bin/bash
for i in $*; do
echo "\$*: ..${i}.."
done; echo
for i in "$*"; do
echo "\"\$*\": ..${i}.."
done; echo
for i in $#; do
echo "\$#: ..${i}.."
done; echo
for i in "$#"; do
echo "\"\$#\": ..${i}.."
done; echo
Running it:
user#host$ ./paramtest abc "space here"
$*: ..abc..
$*: ..space..
$*: ..here..
"$*": ..abc space here..
$#: ..abc..
$#: ..space..
$#: ..here..
"$#": ..abc..
"$#": ..space here..
How about this one:
for file
do
test -f "$file" && ls -l "$file"
done
The for loop by default will work on $#, so you don't have to mention it. Note that you will need to quote "$file" in case if the file name has embedded space. For example, if you save your script to 'myll.sh':
$ myll.sh "My Report.txt" file1 file2
Then "My Report.txt" will be passed in as a whole token instead of 2 separate tokens: "My" and "Report.txt"
The variable you want is indeed $# - this contains all command-line arguments as separate words, each passed on intact (no expansion). ($* treats all of them as a single word - good luck sorting it out if you have spaces in filenames).
You can loop, if you like. This is easily expanded to more complex actions than ls.
for file in "$#"; do
if [ -f "$file" ]; then
ls -l "$file"
fi
done
Note: you should quote $# to protect any special characters inside! You should also quote $file for the same reason - especially inside the test. If there is an empty string in $#, file will also be empty, and without quotes, -f will attempt to act on the ']'. Errors ensue.
Also, if all you need to do is ls (skipping your if) you can just do this:
ls -l "$#"
You could usefully loop over any files specified like this:
for file in "$#"; do
ls -l "$file"
done
If you want to double-check that the name specified is not a directory, you could do this:
for file in "$#"; do
if [ ! -d "$file" ]; then
ls -l "$file"
fi
done
the bash variable for all arguments passed to a script is "$*". Try:
for file in $*; do
if [ -f $file ] ; then
ls -l $file
fi
done
(not tested)

Resources