About accessing file (names) in specific directory and changing it - bash

I'm writing code to exchange the upper and lower alphabet of file's name in certain directory.
If directory is unable to access, it shows err message.
#!/bin/bash
if [ $# -eq 0 ];then
path=$(pwd)
for a in "$path"/*
do
mv "$i" "`echo $i | tr '[:upper:][:lower:]' '[:lower:][:upper:]'`"
done
else
if [ ! -d "$1" ];then
echo "Unable to access directory!"
else
for i in "$1"/*
do
mv "$i" "`echo $i | tr '[:upper:][:lower:]' '[:lower:][:upper:]'`"
done
fi
fi
The problem is that, when I echo $i, it doesn't express ONLY filename, but filename with directory!
So, when I try to mv the file, not only file's name is changed but ALSO directory's name is changed so I can't mv the file.
Like this:
mv: cannot move 'test3/Ipad.txt' to 'TEST3/iPAD.TXT': No such file or directory
mv: cannot move 'test3/iPhone' to 'TEST3/IpHONE': No such file or directory
mv: cannot move 'test3/macOS' to 'TEST3/MACos': No such file or directory
how can I change the file names in certain directory?
Any help would be appreciable and thanks in advance.

I'd use a single loop for handling both cases.
#! /bin/sh -
case $# in
( 0 ) path=. ;;
( * ) path=$1
esac
for fpath in "$path"/*; do
echo mv -- "$fpath" "${fpath%/*}/$(
printf '%s\n' "${fpath##*/}" |
tr '[a-zA-Z]' '[A-Za-z]')"
done
If the path given by user is not present or accessible, this'll error out without causing any harm. But if you insist on handling that yourself, add a check before loop, or, use nullglob with bash and the script will exit silently on such an occasion.
Also note that the possibility that files having neither upper nor lower case letters in their names may exist is ignored here. Nothing will happen to them but mv will complain that source and target are the same.

You may want to cd to the directory first, then work on filenames in the directory:
#!/bin/bash
if [ $# -eq 0 ]; then
path=$(pwd)
cd "$path"
for a in *
do
mv "$i" "`echo $i | tr '[:upper:][:lower:]' '[:lower:][:upper:]'`"
done
cd -
else
if [ ! -d "$1" ]; then
echo "Unable to access directory!"
else
cd "$1"
for i in *
do
mv "$i" "`echo $i | tr '[:upper:][:lower:]' '[:lower:][:upper:]'`"
done
cd -
fi
fi

Related

can't read all file lines in bash pipeline

I searched and couldn't find anything, maybe I can't understand the problem properly.
I have a bash function who read files in current dir and sub dir's, I'm trying to arrange the text and analyze the data but somehow I'm losing lines if I'm using pipeline.
the code:
function recursiveFindReq {
for file in *.request; do
if [[ -f "$file" ]]; then
echo handling "$file"
echo ---------------with pipe-----------------------
cat "$file" | while read -a line; do
if (( ${#line} > 1 )); then
echo ${line[*]}
fi
done
echo ----------------without pipe----------------------
cat "$file"
echo
echo num of lines: `cat "$file" | wc -l`
echo --------------------------------------
fi
done
for dir in ./*; do
if [[ -d "$dir" ]]; then
echo cd to $dir
cd "$dir"
recursiveFindReq "$1"
cd ..
fi
done
}
the output is:
losing lines even when they meet requirements
I marked with 2 red arrows the place I'm losing info

How to accommodate spaces in a variable in a bash and iterate in directory tree in linux

I am writing a bash script to iterate in directory and sub-directories, check if a file opened by a process,
if yes move it to another location
if no skip it
My issue is that the Source folders have Spaces is their names such as "FTP SYNC LOCAL"
my script is able so far to iterate in the folders and subfolders and test if a file is opened by another process.
It only does this if the file name doesn't contain a SPACE in its name, if it does, nothing happened
print_folder_recurse() {
for i in "$1"/* ;do
if [ -d "$i" ];then
echo $i
#lsof "$i" | grep Serv-U | wc -l
print_folder_recurse "$i"
elif [ -f "$i" ]; then
echo $i
flag=$(lsof "$i" | grep Serv-U | wc -l)
if [ $flag == 0 ];then
echo "Done"
elif [ $flag != 0 ];then
echo "Skip Next"
fi
fi
done
}
path=""
if [ -d "$1" ]; then
path=$1;
else
direct="/Source/FTP Sync"
echo $direct
path="$direct"
fi
#echo "base path: $path"
print_folder_recurse $path
The problem is at the bottom of the code with the variable "direct". If I write it
direct="/Source"
echo $direct
path="$direct"
fi
#echo "base path: $path"
print_folder_recurse $path
The file execute.
I can prevent the issue by writing the folder Source/FTP_Sync but I can do this since it will affect a major workflow.
Any help will be apprecaited
the print_folder_recurse functions reads the $path variable as 2 seperate arguments because of the space in between the variable i.e $1 = /Source/Ftp while $2 = Sync. The solution is to wrap the $path variable in double quote like this print_folder_recurse "$path"so that print_folder_recurse can read it as a single argument

Sorting folders with bash

My root folder structure is like this:
My Family - Holiday
My Birthday[15.11]
Name-Name[1]
Name1
Name2
...
Now, I want every folder which contains a .info file to move to another directory.
Here's my code:
#!/bin/sh
for folder in *
do
echo $folder
if [ -e "$folder/*.info" ]
then
echo $folder
mv $folder ./Finished
fi
done
The echoes are only for testing.
I have found that every time a non-escaped character is in the name that the if is failing. How can I fix this?
Bash wants you to quote your variables.
#!/bin/bash
if [[ "$1" = "-v" ]]; then
Verbose=true
vopt="-v"
shift
else
Verbose=false
vopt=""
fi
for folder in *; do
$Verbose && printf "%s" "$folder"
if [[ -e "$folder/*.info" ]]; then
if mv "$vopt" "$folder" ./Finished/; then
$Verbose && echo -n " ... done"
fi
fi
$Verbose && echo ""
done
Note that it's a good idea to end your target directory with a slash. That way, if for some reason the Finished directory disappears, you'll get an error rather than silently renaming the first $folder to Finished, then moving all the other matches into the first match.
Note also that I'm using printf for some of the debugging output just in case one of your $folders starts with a hyphen.
UPDATE #1: you now have debugging controlled with the -v option.
UPDATE #2: I just realized that you are checking for the existence of *.info, literally. Note:
ghoti#pc ~$ mkdir foo
ghoti#pc ~$ touch foo/\*.info
ghoti#pc ~$ ls -la foo
total 0
-rw-r--r-- 1 ghoti ghoti 0 8 Aug 07:45 *.info
drwxr-xr-x 3 ghoti ghoti 102 8 Aug 07:45 .
drwx------+ 10 ghoti ghoti 340 8 Aug 07:44 ..
ghoti#pc ~$ [[ -e "foo/*.info" ]] && echo yes
yes
ghoti#pc ~$ mv foo/\*.info foo/bar.info
ghoti#pc ~$ [[ -e "foo/*.info" ]] && echo yes
ghoti#pc ~$
If what you really want to find is "any file ending in .info", then [[ -e is not the way to go. Pls confirm before I work more on this answer. :)
UPDATE #3:
Here's a version that finds moves your folder if the folder conains any .info file. Note that this does not grep the output of ls.
[ghoti#pc ~/tmp1]$ cat doit
#!/bin/bash
if [[ "$1" = "-v" ]]; then
Verbose=true
vopt="-v"
shift
else
Verbose=false
vopt=""
fi
for folder in *; do
infofile="$(
if [[ -d "$folder" ]]; then
cd "$folder"
for file in *.info; do
if [[ -f "$file" ]]; then
echo "$file"
break
fi
done
fi
)"
if [[ -f "$folder/$infofile" ]]; then
mv "$vopt" "$folder" ./Finished/
elif $Verbose; then
printf "%s ... skipped\n" "$folder"
fi
done
[ghoti#pc ~/tmp1]$ find . -print
.
./doit
./baz
./foo
./foo/bar.info
./Finished
[ghoti#pc ~/tmp1]$ ./doit -v
Finished ... skipped
baz ... skipped
doit ... skipped
foo -> ./Finished/foo
[ghoti#pc ~/tmp1]$
The primary issue is that you cannot use test -e *.info. You can parse the output of ls to check for the file. There are issues with parsing ls, but they are much less significant than many people make them out to be. If you do not allow newlines in filenames, the following should work:
#!/bin/sh
for dir in *; do
if ls -f "$dir" | grep -q '\.info$'; then
mv "$dir" ./Finished
fi
done
Do note that it is essential that no filenames have an embedded newline, since this will incorrectly identify a file named foo.info\nbar as if there were a single file named foo.info. In reality, this is unlikely to be a significant issue. If this is an issue, you will not want to use the shell for this, although you could do:
#!/bin/sh
for dir in *; do
for f in "$dir"/*; do
case "$f" in
*.info) mv "$dir" ./Finished; break;;
esac
done
done
Try this:
PATH_TO_FOLDER="/YOUR_FOLDER"
for f in `ls $PATH_TO_FOLDER`;
do
# Check filename
if [ $(echo $f | grep -Ec ".info$") -ne 1 ]
then
echo "We don't care"
else
## move the file
fi
done

Bash variable assignment

I have a basic script to edit config files in ~/.config - it works with the cd lines, but that seems redundant:
dir=$HOME/.config/$1
if [ ! -d "$dir" ]; then :
else
cd "$dir" &&
for file in * ; do
case "$file" in
conf | config | *.cfg | *rc) $EDITOR "$file" ;;
*) : ;;
esac
done
cd - 1>/dev/null;
fi
Changing it to use the variable "$dir" fails. What am I doing wrong?
dir=$HOME/.config/$1
if [ ! -d "$dir" ]; then :
else
for file in "$dir" ; do
case "$file" in
conf | config | *.cfg | *rc) $EDITOR "$file" ;;
*) : ;;
esac
done;
fi
You're not globbing the files inside $dir, merely listing $dir itself. Try $dir/*.
You can't use just "$dir" because that gives just a single item: the directory. You need $dir/*, but that includes the path as well, so you have to strip that off to compare just the file name:
...
for file in $dir/*; do
filename=`basename $file`
case $filename in
...
Your first version does a * on the directory, yielding a list of all the files in the directory. Your second version just has one entry in the list --- the directory itself, not its contents.

Shell programming, looping through files

I am trying to loop through files in a specified directory. But I can't seem to figure out the logic. I am looping through each file and asking if they want to delete that file.
#!/bin/bash
dirpath=$1
y=y
Y=Y
echo "changing directory '$dirpath' `cd $dirpath`"
for f in $1/*
do
#####################################
if test -f `ls -1 $1`
then
echo -n "remove file '$f' `ls -1` ?"
read answer
##########################
if test $answer = $y || test $answer = $Y
then
echo "Processing $f file..."
echo `rm $f`
echo "file '$f' deleted "
else
echo "file '$f' not removed"
fi#2nd if loop
############################
else
echo 'not a file'
fi#1st if loop
#######################################
done
Your code seems much more complicated that it should be. Does this fulfill your needs or are you doing some shell practice?
rm -iv DIRECTORY/*
There's no need for ls, you already have the filename. Change this:
if test -f `ls -1 $1`
to:
if test -f "$f"
Why are you using echo and backticks here? Change
echo `rm $f`
to:
rm "$f"
Here's another place you're using backticks unnecessarily. Change this:
echo "changing directory '$dirpath' `cd $dirpath`"
to:
echo "changing directory '$dirpath'"
cd "$dirpath"
Always quote variables that contain filenames.
You can have rm do the "asking" for you via its -i flag to prompt user before removal. I am assuming you want to consider only files, not directories, and not recurse any sub-directories.
#!/bin/bash
for f in $1/* ; do
if [ -f $f ] ; then
rm -i $f ;
fi
done
Without the error, can't really help, but it could be written like this, not as verbose though
rm -i *
If $1 is a relative path, then once you've cd'd into $1, the wildcard in your for loop will be meaningless. I'd recommend something more like
cd $1
for f in *; do
...
done
Since it will accept both relative and absolute paths.
Moreover, the arguments to the first test are wrong. Each time through the loop, $f will hold one filename, so your test should be like
if (test -f $f); then
You also repeat this in your echo arguments.
The following does basically what you want, with only slight modifications from your script.
#!/bin/bash
dirpath=$1
y=y
Y=Y
echo "changing directory '$dirpath' `cd $dirpath`"
for f in ./*; do
if (test -f $f); then
echo -n "remove file '$f' ?"
read answer
if (test $answer == $y) || (test $answer == $Y); then
echo "Processing $f file..."
rm $f
echo "file '$f' deleted "
else
echo "file '$f' not removed"
fi
else
echo 'not a file'
fi
done

Resources