Finding a file extension in a string using shell script - bash

I have a long string, which contains a filename somewhere in it. I want to return just the filename.
How can I do this in a shell script, i.e. using sed, awk etc?
The following works in python, but I need it to work in a shell script.
import re
def find_filename(string, match):
string_list = string.split()
match_list = []
for word in string_list:
if match in word:
match_list.append(word)
#remove any characters after file extension
fullfilename = match_list[0][:-1]
#get just the filename without full directory
justfilename = fullfilename.split("/")
return justfilename[-1]
mystr = "the string contains a lot of irrelevant information and then a filename: /home/test/this_filename.txt: and then more irrelevant info"
file_ext = ".txt"
filename = find_filename(mystr, file_ext)
print(filename)
this_filename.txt
EDIT adding shell script requirement
I would call shell script like this:
./test.sh "the string contains a lot of irrelevant information and then a filename: /home/test/this_filename.txt: and then more irrelevant info" ".txt"
test.sh
#!/bin/bash
longstring=$1
fileext=$2
echo $longstring
echo $fileext

With bash and a regex:
#!/bin/bash
longstring="$1"
fileext="$2"
regex="[^/]+\\$fileext"
[[ "$longstring" =~ $regex ]] && echo "${BASH_REMATCH[0]}"
Output:
this_filename.txt
Tested only with your example.
See: The Stack Overflow Regular Expressions FAQ

Considering that you want to get file name with extension and then check if file is present or not in system, if this is the case could you please try following. Adding an additional check which is checking if 2 arguments are NOT passed to script then exit from program.
cat script.bash
if [[ "$#" -ne 2 ]]
then
echo "Please do enter do arguments as per script's need, exiting from program now."
exit 1;
fi
fileName=$(echo "$1" | awk -v ext="$2" 'match($0,/\/[^ :]*/){print substr($0,RSTART,RLENGTH) ext}')
echo "File name with file extension is: $fileName"
if [[ -f "$fileName" ]]
then
echo "File $fileName is present"
else
echo "File $fileName is NOT present."
fi

Related

How do I test if a fasta file exists in bash?

I am working with fasta files in a bash script. Before starting it, I would like to check that I have a file in fasta format.
Let's say I have this file:
>seq1
ASVNJF
>seq2
PNGRW
I was trying
#!/bin/bash
if ! [[ $text_file =~ ^(>).+\n[A-Z\n] ]]
then
echo "It is not a fasta file"
fi
But it is not working. Any ideas for it?
Thank you!!
[[ $text_file =~ ^(>).+\n[A-Z\n] ]] compares the value of $text_file which is probably the file's name, not its contents.
You can use the following Perl one-liner to check the files:
perl -ne '
$id = />.+/;
die "Empty $.\n" if $id && $p || $id && eof;
$p = $id;
die "Invalid char $1 ($.)\n" if !$id && /([^A-Z\n])/
' -- file.seq
It stores whether there is an id on the current line in the variable $id. $p stores the previous $id, which guards against two consecutive id lines. If the current line doesn't contain an id but contains something else than A-Z or a newline, the second error is reported. The special variable $. contains the current input line number.
To make the shell script exit when the Perl command fails, you need to tell the shell script to exit. Just add || exit 1 after the Perl command:
perl -ne '...' -- file.seq || exit 1

Add lines to a document if they do not already exist within the document

I am trying to say, if document does not exist, then create document. Next read each line of the document and if none of the lines match the $site/$name variables, then add the $site/$name variable into the document.
#!/bin/bash
site=http://example.com
doc=$HOME/myfile.txt
if [ ! -f $doc ]
then
touch $doc
fi
read -p "name? " name
while read lines
do
if [[ $lines != $site/$name ]]
then
echo $site/$name >> $doc
fi
done <$doc
echo $doc
echo $site
echo $name
echo $site/$name
echo $lines
Typing test at the read -p prompt the results are
path/to/myfile.txt
http://example.com
test
http://example.com/test
I feel like I should know this but I'm just not seeing it. What am I doing wrong?
If the file is initially empty, you'll never enter the loop, and thus never add the line. If the file is not empty, you'd add your line once for every non-matching line anyway. Try this: set a flag to indicate whether or not to add the line, then read through the file. If you ever find a matching line, clear the flag to prevent the line from being added after the loop.
do_it=true
while read lines
do
if [[ $lines = $site/$name ]]
then
do_it=false
break
fi
done < "$doc"
if [[ $do_it = true ]]; then
echo "$site/$name" >> "$doc"
fi
The following creates the file if it doesn't exist. It then checks to see if it contains $site/$name. If it doesn't find it, it adds the string to the end of the file:
#!/bin/bash
site=http://example.com
doc=$HOME/myfile.txt
read -p "name? " name
touch "$doc"
grep -q "$site/$name" "$doc" || echo "$site/$name" >>"$doc"
How it works
touch "$doc"
This creates the file if it doesn't exist. If it does already exist, the only side-effect of running this command is that the file's timestamp is updated.
grep -q "$site/$name" || echo "$site/$name" >>"$doc"
The grep command sets its exit code to true if it finds the string. If it doesn't find it, then the "or" clause (in shell, || means logical-or) is triggered and the echo command adds the string to the end of the file.

file without extension: how to notice in bash script?

I made a very simple script which tells me a file name and extension.
The script works as follows:
for file in * ; do
if [[ -f $file ]] ; then
filename=${file##*/}
basename=${filename%\.*}
extension=${filename##*.}
if [[ -n $extension ]] ; then
echo "FILE: " $basename " ; ESTENSIONE " $extension
fi
fi
done
The problem is that when I have a file without extension (e.g. Makefile) it says that the extension is the filename itself (e.g. extension= Makefile).
Am I doing something wrong?
Well, the result you get is the expected one; I don't know if that means you're doing something wrong or not.
The way the pattern replacements work is that if the pattern doesn't match, nothing is replaced. Here you have ${filename##*.} which says remove all characters up to and including the final period. But if there's no period in the name, then the pattern doesn't match and nothing is removed, so you simply get the same result as ${filename}.
I should point out that the backslash in ${filename%\.*} is useless: the pattern here is shell globbing not regular expressions, so you don't need to escape a period. You can just write ${filename%.*}.
ETA:
There's no way to do what you want in one step. You have two choices; you can either test to see if the extension is the same as the filename and if so set it to empty:
extension=${filename##*.}
[ "$extension" = "$filename" ] && extension=
or you can strip off the basename, which you already computed, then get rid of any leading periods:
extension=${filename#$basename}
extension=${extension##*.}
Extensions don't have any privileged status in Unix file systems; they are just a part of the file name that people treat specially. You'll have to check if the file contains a . first.
basename=${filename%\.*}
if [[ $filename = *.* ]]; then
extension=${filename##*.}
echo "FILE: " $basename " ; ESTENSIONE " $extension
else
extension=""
fi

How to use a text file for multiple variable in bash

I want to make an bash script for things I use much and for easy access of things but I want to make an firstrun setup that saves the typed paths to programs or commands in a txt file. But how can I do that. And how can I include the lines of the text file to multiple variables?
After a lot of testing I could use the 2 anwsers given. I need to store a variable directly to a textfile and not asking a user for his details and then stores that to a file
So I want it to be like this
if [[ -d "/home/$(whoami)/.minecraft" && ! -L "/home/$(whoami)/.minecraft" ]] ; then
echo "Minecraft found"
minecraft="/home/$(whoami)/Desktop/shortcuts/Minecraft.jar" > safetofile
# This ^ needs to be stored on a line in the textfile
else
echo "No Minecraft found"
fi
if [[ -d "/home/$(whoami)/.technic" && ! -L "/home/$(whoami)/.technic" ]]; then
echo "Technic found"
technic="/home/$(whoami)/Desktop/shortcuts/TechnicLauncher.jar" > safetofile
# This ^ also needs to be stored on an other line in the textfile
else
echo "No Technic found"
fi
I really want to have an anwser to this because I want to script bash. I already experience in bash scripting.
Here's an example:
#!/bin/bash
if [[ -f ~/.myname ]]
then
name=$(< ~/.myname)
else
echo "First time setup. Please enter your name:"
read name
echo "$name" > ~/.myname
fi
echo "Hello $name!"
The first time this script is run, it will ask the user for their name and save it. The next time, it will load the name from the file instead of asking.
#!/bin/bash
# file to save the vars
init_file=~/.init_vars.txt
# save_to_file - subroutine to read var and save to file
# first arg is the var, assumes init_file already exists
save_to_file()
{
echo "Enter $1:"
read val
# check if val has any spaces in them, you will need to quote them if so
case "$val" in
*\ *)
# quote with double quotes before saving to init_file
echo "$1=\"$val\"" >> $init_file
;;
*)
# save var=val to file
echo "$1=$val" >> $init_file
;;
esac
}
if [[ ! -f $init_file ]]
then
# init_file doesnt exist, this will come here only once
# create an empty init_file
touch $init_file
# vars to be read and saved in file, modify accordingly
for var in "name" "age" "country"
do
# call subroutine
save_to_file "$var"
done
fi
# init_file now has three entries,
# name=val1
# age=val2
# country=val3
# source the init_file which will read and execute commands from init_file,
# which set the three variables
. ${init_file}
# echo to make sure it is working
echo $name $age $country

Reading from an archive using Bourne Shell

I am trying to use Bourne shell scripting for the first time ever, and I cannot seem to figure out determining how to save text from a file to internal script variables. The file format is as follows:
acc.text
Jason Bourne 213.4
Alice Deweger 1
Mark Harrington 312
The current script that I have (which might be ENTIRELY incorrect as I am simply creating it in NotePad++ without using an actual shell console) is as follows:
#!/bin/sh
process_file()
{
FILE = $1;
SALARYARRAY;
NAMEARRAY;
COUNTER = 0;
while read line
do
$NAMEARRAY[$COUNTER] =
$SALARYARRAY[$COUNTER] =
$COUNTER + 1;
echo $NAMEARRAY[$COUNTER]:$SALARYARRAY[$COUNTER];
done < "$FILE"
order_Map
}
# Function is not complete as of now, will later order SALARYARRAY in descending order
order_Map()
{
i = 0;
for i in $COUNTER
do
if ($SALARYARRAY[
done
}
##
# Main Script Body
#
# Takes a filename input by user, passes to process_file()
##
PROGRAMTITLE = "Account Processing Shell Script (APS)"
FILENAME = "acc.$$"
echo $PROGRAMTITLE
echo Please specify filename for processing
read $FILENAME
while(! -f $FILE || ! -r $FILE)
do
echo Error while attempting to write to file. Please specify file for processing:
read $FILENAME
done
echo Processing the file...
process_file $FILENAME
I have fixed some of your script. You need to cut out the name and salary fields from each line before storing them into the array.
#!/bin/bash
process_file()
{
file=$1;
counter=0
while read line
do
#the name is the first two fields
name=`echo $line | cut -d' ' -f1,2`
NAMEARRAY[$counter]="$name"
#the salary is the third field
salary=`echo $line | cut -d' ' -f3`
SALARYARRAY[$counter]="$salary"
echo ${NAMEARRAY[$counter]}:${SALARYARRAY[$counter]}
counter=$(($counter+1))
done < $file
}
##
# Main Script Body
#
# Takes a filename input by user, passes to process_file()
##
PROGRAMTITLE="Account Processing Shell Script (APS)"
echo $PROGRAMTITLE
echo -n "Please specify filename for processing: "
read FILENAME
if [ -r $FILENAME ] #check that the file exists and is readable
then
process_file $FILENAME
else
echo "Error reading file $FILENAME"
fi
Given the file format you specified, each record has three fields first last and amount, then:
i=0
while read fistname lastname amount; do
NAMEARRAY[$i]="$firstname $lastname"
SALARYARRAY[$i]=$amount
i = `expr $i + 1`
done < "$FILE"
The shell read built-in, automatically splits input. See the variable IFS in the man page for sh(1). If you have data after the amount field, and you wish to ignore it, just create another variable after amount; but don't use it. It will collect everything after the 1st 3 fields into the extra variable.
You specified Bourne shell, so I used some pretty antiquated stuff:
i=`expr $x + 1`
is usually written
let $((i++)) # ksh, bash (POSIX, Solaris, Linux)
On modern systems, /bin/sh is usually ksh or something pretty compatible. You can probably use let $((i++))

Resources