Use part of filename as variable bash - bash

Background:
I have a bunch of filenames named username.sub in single letter directories under script_testing (first letter of username is the folder name). For every username.sub, I need to check if the line user.$username.contacts exists and, if not, append the line followed by a real tab.
Question:
Given the code I have below, why is it not appending to the file? I think I am missing something simple. I keep getting "contacts already subscribed" even if that line is not there.
#!/bin/bash
Path_to_files=/home/user/script_testing/^[A-z]+$/
FULLNAME="${Path_to_files##*/}"
NAME="${FULLNAME%.*}"
if grep 'contacts' $NAME.sub; then
echo 'contacts already subscribed'
else
echo "subscribing to contacts"
echo -e user.$NAME.Contacts \t >> $NAME.sub
fi

You're grepping for the word contacts - which, depending on what else you have in those files, may always be present.
Instead, use grep -q "^user\.$NAME\.Contacts" to look for your line.

Fixed with the following:
#!/bin/bash
#testing directory
#p=$HOME/script_testing
for f in "$p"/*/*.sub ; do
# if this is a file
if [ -f "$f" ]; then
# define variables
F="${f##*/}"
u="${F%%.*}"
cont=$(grep "user.$u.Contacts" "$f")
cal=$(grep "user.$u.Calendar" "$f")
# if our file doesn't contain Contacts subscription
if [ -z "$cont" ]; then
# add Contacts subscription
echo -e "user.$u.Contacts\t" >> "$f"
#fi
# if our file doesn't contain Calendar subscription
elif [ -z "$cal" ]; then
# add Calendar subscription
echo -e "user.$u.Calendar\t" >> "$f"
fi
fi
done
Also added extra line(s) to append. Please, let me know if there is an issue with this so I can learn, but I haven't encountered any problems.

Related

bash if and then statements both run

I'm running this in bash and even though there is a .txt file it prints out "no new folders to create" in the terminal.
Am I missing something?
FILES=cluster-02/*/*
for f in $FILES
do
if [[ $f == *."txt" ]]
then
cat $f | xargs mkdir -p
else
echo "No new folders to create"
fi
done;
As mentioned in the first comment, the behaviour is indeed as you might expect from your script: you run through all files, text files and other ones. In case your file is a text file, you perform the if-case and in case your file is another type of file, you perform the else-case.
In order to solve this, you might decide not to take the other files into account (only handle text files), I think you might do this as follows:
FILES=cluster-02/*/*.txt
You're looping over multiple files, so the first result may trigger the if and the second can show the else.
You could save the wildcard result in an array, check if there's something in it, and loop if so:
shopt -s nullglob
FILES=( foo/* )
if (( ${#FILES[#]} )); then
for f in "${FILES[#]}"; do
if [[ $f == *."txt" ]]; then
echo $f
fi
done
else
echo "No new folders to create"
fi
#!/usr/bin/env bash
# Create an array containing a list of files
# This is safer to avoid issues with files having special characters such
# as spaces, glob-characters, or other characters that might be cumbersome
# Note: if no files are found, the array contains a single element with the
# string "cluster-02/*/*"
file_list=( cluster-02/*/* )
# loop over the content of the file list
# ensure to quote the list to avoid the same pitfalls as above
for _file in "${file_list[#]}"
do
[ "${_file%.txt}" == "${_file}" ] && continue # skip, not a txt
[ -f "${_file}" ] || continue # check if the file exists
[ -r "${_file}" ] || continue # check if the file is readable
[ -s "${_file}" ] || continue # check if the file is empty
< "${_file}" xargs mkdir -p -- # add -- to avoid issues with entries starting with -
_c=1
done;
[ "${_c}" ] || echo "No new folders to create"

Batch-renaming "complicated" files names

I am having a hard time with this issue.
I've got several files in a folder with the general name format:
[ file type ] file name ( date ) file specs . file extension
File type can be anything such as: Webinar, Presentation, Symposium, Oral Talk etc... Please notice that it is surrounded by [] and might include spaces.
File name can be anything, please notice it might include spaces.
Date is in the general format dd_mm_yyyy. Please notice that it is surrounded by ().
File specs gives general information about the file attributes (and is not important.
I want to write a script so I could rename all files inside the folder to the following format:
date [ file type ] file name . file extension
() around date should be discarded, the date should change for format ddmmyyyy, [] around file type should be maintained, and file specs should be ignored.
Example:
[Oral Talk] Prospects for future research (13_11_2017) 1080p 320kpbs.mp4
should change to:
13112017 [Oral Talk] Prospects for future research.mp4
But then, this should be iterated for all files in the folder.
#!/usr/bin/env bash
# use a regular expression with match groups to pick out pieces of the name
re='\[(.+)\] (.+) [(]([[:digit:]_]+)[)](.*)[.]([[:alnum:]]+)'
# 1 2 3 4 5
# if we were passed a directory name, change to it before running
if [[ $1 ]]; then cd -- "$1" || exit; fi
for name in *.*; do
if [[ $name =~ $re ]]; then
date=${BASH_REMATCH[3]}
ext=${BASH_REMATCH[5]}
type=${BASH_REMATCH[1]}
topic=${BASH_REMATCH[2]}
new_name="${date//_/} [$type] $topic.$ext"
printf 'Renaming %q to %q\n' "$name" "$new_name" >&2
[[ $dry_run ]] || mv -- "$name" "$new_name"
else
printf 'Filename %q does not match pattern; ignoring\n' "$name" >&2
fi
done
If saved under the name renaming-script and run as:
dry_run=1 ./renaming-script directory-to-rename
...this will merely print a report of which contents it would rename. With the dry_run=1 removed, it actually performs those rename operations.

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.

Bash check is file with variable name inside loop exists

I would like to check if a file exists. Of course this is explained in many places. Now I am inside a loop like:
for ((l=0;l<5;l+=1));
do
if -a FILENAMEl #FILENAME contains l!!!!!!!!!
then "FILENAMEl exists"
else
do
.............
fi
done
Any ideas?
Thanks so much!!!
The main problem is that you are mixing the syntax of a variable name l with that of a file name. If you wish to use them together, to form part of a filename with a variable, you need a syntax break (caused by "$"), or use braces ({}).
If the file name has a variable in the middle, then braces work best. For example: "my_file_${l}_head.txt" would create files like my_file_1_head.txt, my_file_2_head.txt, etc.
Here is your original example corrected:
for ((l=0;l<5;l+=1))
do
if test -a FILENAME$l
then echo "FILENAME$l exists"
else echo "FILENAME$l doesn't exist"
fi
done
However, I wouldn't write code this way.
I only took your example and changed it as little as possible to show you the essential difference.
Here's another way to write it, using a more DRY (Don't Repeat Yourself) approach:
for l in {1..5}; do
file="filename$l"
if [[ -a "$file" ]]; then
echo "$file exists"
else
echo "$file does not exist"
fi
done
If you want more minimalism, here's yet another approach:
for l in {1..5}; do f="filename$l"
[[ -a "$f" ]] && echo "$f exists" || echo "$f does not exist"
done
Now, if you need to do something other than just print out the status, using function calls to make the extra work modular works well:
for l in {1..5} ; do f="$filename$l"
[[ -a "$f" ] && process_file $f || non_existant_file $f
done
Then, elsewhere, you should define both process_file and non_existant_file:
process_file() {
local file="$1"
# do whatever is needed for an existing file
}
non_existant_file() {
local file="$1"
# do whatever is needed for a non-existant file
}
Assume you're trying to find which files exist in the filename format file1.csv, file2.csv, etc...
for i in {1..5};
do f="file$i.csv";
if test -e $f;
then echo "$f exists";
else echo "$f does not exist";
fi
done
Perhaps what you need is simply a find
find . -name "file?.csv" -size +10k
You can restrict the file name to suffix 1..5 and do an action on the find result (check for find's -exec or more generally xargs as below).
find . -name "file[1-5].csv" -size +10c | xargs head -1

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

Resources