Linux Bash Script - match lower case path in argument with actual filesystem path - linux

I have a linux script that gets an argument passed to it that originates from MSDOS (actually DOSEMU running MS DOS 6.22). The argument that gets passed is case insensitive (as DOS didn't do cases) but of course Linux does.
I am trying to get from the following passed argument
/media/zigg4/vol1/database/scan/stalbans/docprint/wp23452.wpd
to
/media/zigg4/vol1/Database/SCAN/STALBANS/DOCPRINT/Wp23452.WPD
I do not know the actual case sensitive path so I need to somehow determine it from the argument that is passed to the script. I have absolutely no idea where to start with this so any help is greatly appreciated.
edited for extra information and clarity
UPDATE
Thanks to the answer by #anubhava I used the following:-
#!/bin/bash
copies=1
if [ ! -z "$2" ]; then
copies=$2
fi
find / -readable -ipath $1 2>&1 | grep -v "Permission denied" | while IFS= read -r FILE; do
lpr -o Collate=True -#$copies -sP $FILE
done
Works great :-)

You can use -ipath option of find for ignore case path matching:
# assuming $arg contains path argument supplied
find . -ipath "*$arg*"

I would employ awk for this (of course without salary)
#!/bin/bash
awk -varg="$1" -vactual="/media/zigg4/vol1/Database/SCAN/STALBANS/DOCPRINT/Wp23452.WPD" 'BEGIN{
if (tolower(arg)==tolower(actual)){
printf "Argument matches actual filepath\n"
}
}'
Run the script as
./script "/media/zigg4/vol1/database/scan/stalbans/docprint/wp23452.wpd"

Something like this:
if [ "$( echo $real | tr A-Z a-z )" = "$lower" ]; then
echo "matchy"
else
echo "no is matchy"
fi
Some notes:
tr is doing a to-lower translate.
The $( ... ) bit is placing the result of the enclosed command into a string.
You could do the translate on either side if you aren't sure if your "lower case" string can be trusted...

Related

Echo to all files found from GREP

I'm having a trouble with my code.
grep looks for files that doesn't have a word 'code'
and I need to add 'doesn't have' as a last line in those files
By logic
echo 'doesnt have' >> grep -ril 'code' file/file
I'm using -ril to ignore the cases and get file names
Does anyone know how to append a text to each .txt files found from grep searches?
How's this for a novel alternative?
echo "doesn't have" |
tee -a $(grep -riL 'code' file/file)
I switched to the -L option to list the files which do not contain the search string.
This is unfortunately rather brittle in that it assumes your file names do not contain whitespace or other shell metacharacters. This can be fixed at the expense of some complexity (briefly, have grep output zero-terminated matches, and read them into an array with readarray -d ''. This requires a reasonably recent Bash, and probably GNU grep.)
The 'echo' command can append output to a single file, which must be specified by redirecting the standard output. To update multiple files, a loop is needed. The loop iterated over all the files found with 'grep'
for file in $(grep -ril 'code' file/file) ; do
echo 'doesnt have' >> $file
done
Using a while read loop and Process Substitution.
#!/usr/bin/env bash
while IFS= read -r files; do
echo "doesn't have" >> "$files"
done < <(grep -ril 'code' file/file)
As mentioned by #dibery
#!/bin/sh
grep -ril 'code' file/file | {
while IFS= read -r files; do
echo "doesn't have" >> "$files"
done
}

Bash: Cut current path until a certain folder

lets say I have three bash files in different directories:
/a/b/c/d/e/f/script1.sh
/a/bb/c/d/script2.sh
/aa/b/c/d/e/f/g/h/script3.sh
If I call $(pwd) I get the path of the current directory. Is there a way to somehow "crop" this path until a certain folder? In the following an example is shown if the certain folder would be called "c":
In the case of script1.sh I would like to have the path: /a/b/c
In the case of script2.sh I would like to have the path: /a/bb/c
In the case of script3.sh I would like to have the path: /aa/b/c
Thank you for your help
I assume what you want is parameter expansion :
$ path="/a/b/c/d/e/f/script1.sh"
$ echo "${path#*/c}"
/d/e/f/script1.sh
Edit
Inversed :
$ path="/a/b/c/d/e/f/script1.sh"
$ echo "${path%/d*}"
/a/b/c
Regards!
Use cut command:
echo '/a/b/c/d/e/f/script1.sh' | cut -d '/' -f 1-4
echo '/a/bb/c/d/script2.sh' | cut -d '/' -f 1-4
echo '/aa/b/c/d/e/f/g/h/script3.sh' | cut -d '/' -f 1-4
Bash regex:
#!/bin/bash
[[ "$PWD" =~ (^/[^/]+/[^/]+/[^/]+)(.*) ]] && echo ${BASH_REMATCH[1]}
It returns the first three components of your path (if there are three components). You could also set the path tp for example $pwd and:
$ pwd=/a/b/c/d/e/f/script1.sh
$ [[ "$pwd" =~ (^/[^/]+/[^/]+/[^/]+)(.*) ]] && echo ${BASH_REMATCH[1]}
/a/b/c
Also, pay notice to #123's comment below; that is the correct way, my mind was off. Thank you, sir.
Given a situation like this:
$ pwd
/a/b/c/d/cc/e/c/f
$ FOLDER=c
You could use shell parameter expansion on the $PWD variable like this:
$ echo "${PWD%/${FOLDER}/*}/${FOLDER}"
/a/b/c/d/cc/e/c
$ echo "${PWD%%/${FOLDER}/*}/${FOLDER}"
/a/b/c
The difference is in the single or double %. With % the result of the expansion is the expanded value of parameter (${PWD}) with the shortest matching pattern. With %% it will be the longest matching pattern.
Just make sure you'll enclose the ${FOLDER} variable with forward slashes, because otherwise it could match directories that contain the match in their name (like in this example the directory cc)
Because you would like to include the folder you were looking for, it's included at the end of the string, prefixed with a forward slash.
Try this:
[sahaquiel#sahaquiel-PC h]$ pwd
/home/sahaquiel/a/b/c/d/e/f/g/h
[sahaquiel#sahaquiel-PC h]$ pwd | cut -d"/" -f1-6
/home/sahaquiel/a/b/c

Linux : check if something is a file [ -f not working ]

I am currently trying to list the size of all files in a directory which is passed as the first argument to the script, but the -f option in Linux is not working, or am I missing something.
Here is the code :
for tmp in "$1/*"
do
echo $tmp
if [ -f "$tmp" ]
then num=`ls -l $tmp | cut -d " " -f5`
echo $num
fi
done
How would I fix this problem?
I think the error is with your glob syntax which doesn't work in either single- or double-quotes,
for tmp in "$1"/*; do
..
Do the above to expand the glob outside the quotes.
There are couple more improvements possible in your script,
Double-quote your variables to prevent from word-splitting, e.g. echo "$temp"
Backtick command substitution `` is legacy syntax with several issues, use the $(..) syntax.
The [-f "filename"] condition check in linux is for checking the existence of a file and it is a regular file. For reference, use this text as reference,
-b FILE
FILE exists and is block special
-c FILE
FILE exists and is character special
-d FILE
FILE exists and is a directory
-e FILE
FILE exists
-f FILE
FILE exists and is a regular file
-g FILE
FILE exists and is set-group-ID
-G FILE
FILE exists and is owned by the effective group ID
I suggest you try with [-e "filename"] and see if it works.
Cheers!
At least on the command line, this piece of script does it:
for tmp in *; do echo $tmp; if [ -f $tmp ]; then num=$(ls -l $tmp | sed -e 's/ */ /g' | cut -d ' ' -f5); echo $num; fi; done;
If cut uses space as delimiter, it cuts at every space sign. Sometimes you have more than one space between columns and the count can easily go wrong. I'm guessing that in your case you just happened to echo a space, which looks like nothing. With the sed command I remove extra spaces.

Unmatching parentheses in awk command inside bash script causing syntax error

This is the error I get after compiling and running the file
./skript05.sh: 13: ./skript05.sh: Syntax error: word unexpected (expecting ")")
Here's the important snippet of the code, with line 13 pointed out. I don't think the rest of the code is needed since it works well, but if it is, let me know.
ls -l $1 | awk "
if($1 ~ /-([r-][w-][x-]){3}/)
{
MUCHLINES=$(system(\"egrep -o \"^[a-z ]{1,}$\" \", $9) | wc -l) ;#13
test -z "$MUCHLINES" && continue ;
print $9":"$MUCHLINES ;
>&2 echo "$9":"yes" ;
}
else >&2 echo "$9":"no" ;
"
What I can figure out is that it ignores symbols {} in lines 12 and 17, but I can't figure out why. All other parentheses are not ignored and highlighted properly when editing code in gedit for example.
I'm working on this in Linux.
I've been searching for answers on this for days, but can't seem to find any. If there are any, I'll apologize and delete this question. Thank you for any help!
(In case you need to know what is this code supposed to do — it should print into stdout "(nameoffile):(x)", where x is number of lines made up of only lowercase letters and spaces; and print into stderr "(nameoffile):yes/no" with yes in case of files (in ls -l known by having - as first symbol in access rights).)
#EdW - As others have pointed out, the script that you posted is a jumble, but I thought that the following might be helpful to you in that it does illustrate how one can combine ls, awk, and egrep in a bash script, more or less along the lines you seem to have had in mind. Please note, however, that it does NOT address some of the issues that others have raised, nor is it intended to serve as a model. It probably does not even do precisely what you want, but I hope you find it helpful in achieving some of your goals.
#!/bin/bash
ls -l $1 | awk '{print $1, $9}' | while read mask file
do
if [[ $mask =~ -([r-][w-][x-]){3} ]] ; then
MUCHLINES=$(egrep -o "^[a-z ]{1,}$" "$file" | wc -l)
if [[ $MUCHLINES -gt 0 ]] ; then
echo "$file:" $MUCHLINES
>&2 echo "$file:yes"
else
>&2 echo "$file:no"
fi
fi
done

Bash loop through directory including hidden file

I am looking for a way to make a simple loop in bash over everything my directory contains, i.e. files, directories and links including hidden ones.
I will prefer if it could be specifically in bash but it has to be the most general. Of course, file names (and directory names) can have white space, break line, symbols. Everything but "/" and ASCII NULL (0×0), even at the first character. Also, the result should exclude the '.' and '..' directories.
Here is a generator of files on which the loop has to deal with :
#!/bin/bash
mkdir -p test
cd test
touch A 1 ! "hello world" \$\"sym.dat .hidden " start with space" $'\n start with a newline'
mkdir -p ". hidden with space" $'My Personal\nDirectory'
So my loop should look like (but has to deal with the tricky stuff above):
for i in * ;
echo ">$i<"
done
My closest try was the use of ls and bash array, but it is not working with, is:
IFS=$(echo -en "\n\b")
l=( $(ls -A .) )
for i in ${l[#]} ; do
echo ">$i<"
done
unset IFS
Or using bash arrays but the ".." directory is not exclude:
IFS=$(echo -en "\n\b")
l=( [[:print:]]* .[[:print:]]* )
for i in ${l[#]} ; do
echo ">$i<"
done
unset IFS
* doesn't match files beginning with ., so you just need to be explicit:
for i in * .[^.]*; do
echo ">$i<"
done
.[^.]* will match all files and directories starting with ., followed by a non-. character, followed by zero or more characters. In other words, it's like the simpler .*, but excludes . and ... If you need to match something like ..foo, then you might add ..?* to the list of patterns.
As chepner noted in the comments below, this solution assumes you're running GNU bash along with GNU find GNU sort...
GNU find can be prevented from recursing into subdirectories with the -maxdepth option. Then use -print0 to end every filename with a 0x00 byte instead of the newline you'd usually get from -print.
The sort -z sorts the filenames between the 0x00 bytes.
Then, you can use sed to get rid of the dot and dot-dot directory entries (although GNU find seems to exclude the .. already).
I also used sed to get read of the ./ in front of every filename. basename could do that too, but older systems didn't have basename, and you might not trust it to handle the funky characters right.
(These sed commands each required two cases: one for a pattern at the start of the string, and one for the pattern between 0x00 bytes. These were so ugly I split them out into separate functions.)
The read command doesn't have a -z or -0 option like some commands, but you can fake it with -d "" and blanking the IFS environment variable.
The additional -r option prevents a backslash-newline combo from being interpreted as a line continuation. (A file called backslash\\nnewline would otherwise be mangled to backslashnewline.) It might be worth seeing if other backslash-combos get interpreted as escape sequences.
remove_dot_and_dotdot_dirs()
{
sed \
-e 's/^[.]\{1,2\}\x00//' \
-e 's/\x00[.]\{1,2\}\x00/\x00/g'
}
remove_leading_dotslash()
{
sed \
-e 's/^[.]\///' \
-e 's/\x00[.]\//\x00/g'
}
IFS=""
find . -maxdepth 1 -print0 |
sort -z |
remove_dot_and_dotdot_dirs |
remove_leading_dotslash |
while read -r -d "" filename
do
echo "Doing something with file '${filename}'..."
done
It may not be the most favorable way but I tried bellow thing
while read line ; do echo $line; done <<< $(ls -a | grep -v -w ".")
check the below trail which I did
Try the find command, something like:
find .
That will list all the files in all recursive directories.
To output only files excluding the leading . or .. try:
find . -type f -printf %P\\n

Resources