How to match file extension in bash? [duplicate] - string

This question already has answers here:
How to check the extension of a filename in a bash script?
(11 answers)
Closed 4 years ago.
I want to call a function ONLY when the file name ends with .request but somehow it calls the function if request is sub-string.
I marked with red and green the imported parts, on the right it's the output
for file in ${filesOrDir[*]}; do
if [[ -f "$file" ]]; then
if [[ "$file"=*[".request"] ]]; then
# Enters here when .request is a substring.
fi
fi
if [[ -d "$file" ]]; then
# ... some logics
fi
done

Try to use: [[ $file == *.request ]]
handle-request.sh is a helping script for checking file names.
handle-request.sh:
while IFS='' read -r line || [[ -n "$line" ]]; do
if [[ $line == *.request ]]; then
echo $line
fi
done < "$1"
Explanation: reference
IFS='' (or IFS=) prevents leading/trailing whitespace from being trimmed.
-r prevents backslash escapes from being interpreted.
|| [[ -n $line ]] prevents the last line from being ignored if it doesn't end with a \n (since read returns a non-zero exit code when it encounters EOF).
input file:
hello.request
.request.hello
file name with space.request
Output:
hello.request
file name with space.request

Related

Unix - Replace column value inside while loop

I have comma separated (sometimes tab) text file as below:
parameters.txt:
STD,ORDER,ORDER_START.xml,/DML/SOL,Y
STD,INSTALL_BASE,INSTALL_START.xml,/DML/IB,Y
with below code I try to loop through the file and do something
while read line; do
if [[ $1 = "$(echo "$line" | cut -f 1)" ]] && [[ "$(echo "$line" | cut -f 5)" = "Y" ]] ; then
//do something...
if [[ $? -eq 0 ]] ; then
// code to replace the final flag
fi
fi
done < <text_file_path>
I wanted to update the last column of the file to N if the above operation is successful, however below approaches are not working for me:
sed 's/$f5/N/'
'$5=="Y",$5=N;{print}'
$(echo "$line" | awk '$5=N')
Update: Few considerations which need to be considered to give more clarity which i missed at first, apologies!
The parameters file may contain lines with last field flag as "N" as well.
Final flag needs to be update only if "//do something" code has successfully executed.
After looping through all lines i.e, after exiting "while loop" flags for all rows to be set to "Y"
perhaps invert the operations do processing in awk.
$ awk -v f1="$1" 'BEGIN {FS=OFS=","}
f1==$1 && $5=="Y" { // do something
$5="N"}1' file
not sure what "do something" operation is, if you need to call another command/script it's possible as well.
with bash:
(
IFS=,
while read -ra fields; do
if [[ ${fields[0]} == "$1" ]] && [[ ${fields[4]} == "Y" ]]; then
# do something
fields[4]="N"
fi
echo "${fields[*]}"
done < file | sponge file
)
I run that in a subshell so the effects of altering IFS are localized.
This uses sponge to write back to the same file. You need the moreutils package to use it, otherwise use
done < file > tmp && mv tmp file
Perhaps a bit simpler, less bash-specific
while IFS= read -r line; do
case $line in
"$1",*,Y)
# do something
line="${line%Y}N"
;;
esac
echo "$line"
done < file
To replace ,N at the end of the line($) with ,Y:
sed 's/,N$/,Y/' file

bash script while loop with read input from user [duplicate]

This question already has answers here:
How to create a dynamic variable and assign value to it?
(2 answers)
Closed 5 years ago.
Wondering if there is an easy way to do what im attempting and the best way to go about something like this.
answer=y
while [ "$answer" = y ]
do
echo "type name of the file you wish to delete"
IFS= read -r file
echo "would you like to delete another file [y/n]?"
IFS= read -r answer
done
echo "Exiting Program"
exit 0
My questions here is when an input is entered it gets stored in file but I would like when the answer is y that it can then get stored to another variable like file1.
Im then left with variables containing all the filenames someone wishes to delete which I can echo back.
How can I use a loop in this to keep adding the input until someone types n
Thanks for the help.
I wouldn't use a realtime read loop to delete files. Too many things can go wrong. Rather, maybe take a file of input and check each.
But for a simplistic answer to your question -
typeset -l answer=y # force to lowercase
lst=/tmp/file.list.$$
while [[ y == "$answer" ]]
do
echo "type name of the file you wish to delete"
IFS= read -r file
if [[ -e "$file" ]]
then echo "$file" >> $lst
else echo "That file does not exist/is not accessible."
fi
echo "would you like to delete another file [y/n]?"
IFS= read -r answer
done
echo -e "Files to be deleted:\n===\n$(<$lst)"
echo -e "\n===\n Delete all listed files [y/n]?"
IFS= read -r answer
if [[ y == "$answer" ]]
then rm $(<$lst)
fi
rm $lst
Maybe this helps:
while :; do
read -s -n 1 answer
if [ $answer = "y" || $answer = "Y" ]; then <your code>
elif [ $answer = "n" || $answer = "N" ]; then echo; echo; break
elif [ $answer = "" ]; then continue # pressed: return key
fi
done
depending the shell you use, you might use: [[ ${answer} =~ (Y|y) ]]

Linux shell script regex match

I have a text file.
I want to get lines starting with specific format.
I just want to get lines that have x/x/x format. x is a number.
But this regex is not working. It is always giving no match :
while read line
do
regex="\d+\/\d+\/\d+"
if [[ ${line} =~ ${regex} ]]; then
echo ${line}
else
echo "no match : ${line}"
fi
done <${textFileName}
File is :
Don't use bash if you can use a better tool:
grep -E '^[[:digit:]]+/[[:digit:]]+/[[:digit:]]+' "${textFileName}"
But if you have to use bash:
while IFS= read -r line
do
if [[ "$line}" =~ ^[[:digit:]]+/[[:digit:]]+/[[:digit:]]+ ]]; then
echo -- "$line"
else
echo "no match: $line"
fi
done < "$textFileName"
\d is not valid regex(3).

Check if line starts with digit then put those lines in a separate file using Bash

I would like to check in bash if line starts with digit then put those lines in a separate file. I tried ^[[0-9]] but it doesn't work.
Here is the code which I tried:
myFailedFile=/tmp/1.txt
myTestsFile:
177711 TESTING ...yahoo.tests.calendar.resources.scheduler.1
854756 TESTING ...yahoo.tests.calendar.resources.scheduler.2
* 2102637 DONE ...yahoo.tests.mail.contacts.add.3
while read line
do
if ( [[ ${line} = "^[[0-9]]"* ]] && [[ ${line} = *".tests."* ]] ); then
echo -e "${line}\r" >> ${myFailedFile}
fi
done <"${myTestsFile}"
Expected output of myFailedFile:
177711 TESTING ...yahoo.tests.calendar.resources.scheduler.1
854756 TESTING ...yahoo.tests.calendar.resources.scheduler.2
The correct operator to use regex in Bash script is =~. Moreoever you don't need to double [ in range of characters. Try this:
while read line
do
if ( [[ ${line} =~ ^[0-9] ]] && [[ ${line} = *".tests."* ]] ); then
echo -e "${line}\r" >> ${myFailedFile}
fi
done <"${myTestsFile}"
Edit:
But you don't need a Bash loop for that job. You can do it with a sed one-liner:
sed '/^[0-9].*\.tests\./!d' "${myTestsFile}" > myFailedFile
Explanations(from right to left):
!d: do not delete
/^[0-9].*\.tests\./: all lines that start with one or more digits and that contain .tests. string
Without using regex you can use glob as this:
[[ $line = [0-9]* ]] && [[ $line = *".tests."* ]]
[0-9]* matches a string start start with digits
*".tests."* matches a string that contains .tests.
you can use
if [[ ${line} =~ ^[0-9] && ${line} =~ ".tests." ]] ; then
or
if [[ ${line} == [0-9]* && ${line} == *".tests."* ]] ; then

Bash scripting: why is the last line missing from this file append?

I'm writing a bash script to read a set of files line by line and perform some edits. To begin with, I'm simply trying to move the files to backup locations and write them out as-is, to test the script is working. However, it is failing to copy the last line of each file. Here is the snippet:
while IFS= read -r line
do
echo "Line is ***$line***"
echo "$line" >> $POM
done < $POM.backup
I obviously want to preserve whitespace when I copy the files, which is why I have set the IFS to null. I can see from the output that the last line of each file is being read, but it never appears in the output.
I've also tried an alternative variation, which does print the last line, but adds a newline to it:
while IFS= read -r line || [ -n "$line" ]
do
echo "Line is ***$line***"
echo "$line" >> $POM
done < $POM.backup
What is the best way to do this do this read-write operation, to write the files exactly as they are, with the correct whitespace and no newlines added?
The command that is adding the line feed (LF) is not the read command, but the echo command. read does not return the line with the delimiter still attached to it; rather, it strips the delimiter off (that is, it strips it off if it was present in the line, IOW, if it just read a complete line).
So, to solve the problem, you have to use echo -n to avoid adding back the delimiter, but only when you have an incomplete line.
Secondly, I've found that when providing read with a NAME (in your case line), it trims leading and trailing whitespace, which I don't think you want. But this can be solved by not providing a NAME at all, and using the default return variable REPLY, which will preserve all whitespace.
So, this should work:
#!/bin/bash
inFile=in;
outFile=out;
rm -f "$outFile";
rc=0;
while [[ $rc -eq 0 ]]; do
read -r;
rc=$?;
if [[ $rc -eq 0 ]]; then ## complete line
echo "complete=\"$REPLY\"";
echo "$REPLY" >>"$outFile";
elif [[ -n "$REPLY" ]]; then ## incomplete line
echo "incomplete=\"$REPLY\"";
echo -n "$REPLY" >>"$outFile";
fi;
done <"$inFile";
exit 0;
Edit: Wow! Three excellent suggestions from Charles Duffy, here's an updated script:
#!/bin/bash
inFile=in;
outFile=out;
while { read -r; rc=$?; [[ $rc -eq 0 || -n "$REPLY" ]]; }; do
if [[ $rc -eq 0 ]]; then ## complete line
echo "complete=\"$REPLY\"";
printf '%s\n' "$REPLY" >&3;
else ## incomplete line
echo "incomplete=\"$REPLY\"";
printf '%s' "$REPLY" >&3;
fi;
done <"$inFile" 3>"$outFile";
exit 0;
After review i wonder if :
{
line=
while IFS= read -r line
do
echo "$line"
line=
done
echo -n "$line"
} <$INFILE >$OUTFILE
is juts not enough...
Here my initial proposal :
#!/bin/bash
INFILE=$1
if [[ -z $INFILE ]]
then
echo "[ERROR] missing input file" >&2
exit 2
fi
OUTFILE=$INFILE.processed
# a way to know if last line is complete or not :
lastline=$(tail -n 1 "$INFILE" | wc -l)
if [[ $lastline == 0 ]]
then
echo "[WARNING] last line is incomplete -" >&2
fi
# we add a newline ANYWAY if it was complete, end of file will be seen as ... empty.
echo | cat $INFILE - | {
first=1
while IFS= read -r line
do
if [[ $first == 1 ]]
then
echo "First Line is ***$line***" >&2
first=0
else
echo "Next Line is ***$line***" >&2
echo
fi
echo -n "$line"
done
} > $OUTFILE
if diff $OUTFILE $INFILE
then
echo "[OK]"
exit 0
else
echo "[KO] processed file differs from input"
exit 1
fi
Idea is to always add a newline at the end of file and to print newlines only BETWEEN lines that are read.
This should work for quite all text files given they are not containing 0 byte ie \0 character, in which case 0 char byte will be lost.
Initial test can be used to decided whether an incomplete text file is acceptable or not.
Add a new line if line is not a line. Like this:
while IFS= read -r line
do
echo "Line is ***$line***";
printf '%s' "$line" >&3;
if [[ ${line: -1} != '\n' ]]
then
printf '\n' >&3;
fi
done < $POM.backup 3>$POM

Resources