How to refer to variable in bash script - linux

I have a test folder containing two files: test.txt and test2.txt. I want to copy these files from test to the test2 directory using a for loop and bsub. Here is the script I wrote, which is called looptest.sh.
looptest.sh
#!/bin/bash
cp ~/test/$F ~/test2
This is the for loop I typed in the command line:
for F in 'ls -l' ; do
bsub -n 2 -q short looptest.sh "$F" ;
done
I run the command from the test folder, but it's not working, and I think it has to do with the way I refer to "F". Any thoughts?

Related

Concatenate (using bash) all file names in subdirectories with option

I have directory work_dir, and there are some subdirectories inside. And inside subdirectories there are zip archives. I can see all zip archives in terminal:
find . -name *.zip
The output:
./folder2/sub/dir/test2.zip
./folder3/test3.zip
./folder1/sub/dir/new/test1.zip
Now I want to concatinate all these file names in single row with some option. For example I want single row:
my_command -f ./folder2/sub/dir/test2.zip -f ./folder3/test3.zip -f ./folder1/sub/dir/new/test1.zip -u user1 -p pswd1
In this example:
my_command is some command
-f the option
-u user1 another option with value
-p pswd1 another option with value
Can you help me please, how can I do this in Linux BASH ?
One way is: (updated per #M. Nejat Aydin comments)
find . -name "*.zip" -print0 | xargs -0 -n1 printf -- '-f\0%s\0' | xargs -0 -n100000 my_command -u user1 -p pswd1
Note that -n100000 parameter forces all output of the previous xargs to be executed on the same line with the assumption that number of findings will be less than 100000.
I used null terminated versions (notice: -0 flag, -print0) because file names can contain spaces.
This is a bash script that should do what you wanted.
#!/usr/bin/env bash
user=user1
passwd=pswd1
while IFS= read -rd '' files; do
args+=(-f "$files")
done < <(find . -name '*.zip' -print0)
args=("${args[#]}" -u "$user" -p "$passwd")
##: Just for the human eye to see the output,
##: change this line of code according to the comment below.
printf 'mycommand %s\n' "${args[*]}"
The output should be in one-line, like what you wanted, but do change the last line from
printf 'mycommand %s\n' "${args[*]}"
into
mycommand "${args[#]}"
If you actually want to execute mycommand with the arguments.
Change the value of user and passwd too.
A while + read loop was used with IFS.
See How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?
Why the last line should be change.
See Arguments
Shell quoting is a basic but common mistake when dealing with spaces in file/path name.
See How can I find and safely handle file names containing
Also the find command/utiliy.
The construct "${args[#}" is an array.
See Array1 Array2 Array3
You can do this by making a bash script.
Make a new file called whatever.sh
Type chmod +x ./whatever.sh so it becomes executable on the terminal
Add the BASH scripting as shown below..
#!/bin/bash
# Get all the zip files from your FolderName
files="`find ./FolderName -name *.zip`"
# Loop through the files and build your args
arg=""
for file in $files; do
arg="$arg -f $file"
done
# Run your command
mycommand $arg -u user1 -p pswd1

Copy a file from a directory and paste it to multiple sub directories in linux(ubuntu) terminal?

I have a directory mnt/d/LIVI.
Inside the directory, LIVI, I have sub-directories:
mnt/d/LIVI/ak
mnt/d/LIVI/ag
mnt/d/LIVI/few
mnt/d/LIVI/ww4
mnt/d/LIVI/ks5
I wanted to copy a file named tt.txt from mnt/d/LIVI/ak/tt.txt and paste to all the sub directories of LIVI, using Ubuntu terminal. How do i do it using a shell script file?
I tried the following one, but it didn't work.
I created a text file named mnt/d/LIVI/FOLDERS.txt, This listed all the sub directories names.
And saved a script file in mnt/d/LIVI directory. The following is the script
#!/bin/sh
# -*- coding: utf-8-unix -*-
ROOTDIR=$(dirname $0 | xargs readlink -f)
for SIMDIR in cat FOLDERS.txt | xargs readlink -f ; do
cp mnt/d/LIVI/ak/tt.txt $SIMDIR
done
#cd ..
date
You may try this bash script
#!/bin/bash
cd "${0%/*}" || exit
for dir in */; do
if [[ $dir != 'ak/' ]]; then
cp ak/tt.txt "$dir"
fi
done
The script must reside under the diectory mnt/d/LIVI
Don't read lines with for.
(If you really wanted to, the syntax for that would look like
for dir in $(cat FOLDERS.txt); do
...
but really, don't. The link above explains why in more detail.)
I don't see why you want to run readlink on the directories at all?
cd "$(dirname "$0")"
while read -r dir; do
cp ak/tt.txt "$dir"
done <FOLDERS.txt
Note also Correct Bash and shell script variable capitalization

Increment the title of files output by a command in a shell script

I made this simple bash script to take a full-screen screenshot and save it to the pictures folder
#!/usr/bin/bash
xfce4-screenshooter -f -s /home/rgcodes/Pictures/Screenshot_$scrshotcount.png
let "scrshotcount++"
...which runs into a problem. scrshotcount is a global variable I defined in /etc/environment to be incremented every time the script runs. However, the script fails to increment the variable globally, and causes the script to just overwrite the previous screenshot. Searches on Google and Stack Overflow revealed that the problem isn't straightforward at all (something about child shells being unable to change variables for parents), and finding some other method would be better.
Here's my question. How do we append numbers (in ascending order) to the screenshots the script throws out so that they are saved just like those taken on Windows?(Windows auto-suffixes matching filenames, rather than overwriting them, so all Screenshots have the same name 'Screenshot' and the number of times the screenshot command has been used.)
I am using #erikMD's method as a temporary stop-gap for now.
In addition to the excellent advice about using a date instead of a counter, here's a way to use a counter :/
dir=$HOME/Pictures
# find the current maximum value
current_max=$(
find "$dir" -name Screenshot_\*.png -print0 \
| sort -z -V \
| tail -z -n 1
)
if [[ ! $current_max =~ _([0-9]+)\.png ]]; then
echo "can't find the screenshot with the maximum counter value" >&2
exit 1
fi
# increment it
counter=$(( 1 + ${BASH_REMATCH[1]} ))
# and use it
xfce4-screenshooter -f -s "$dir/Screenshot_${counter}.png"
You'll have to manually create the Screenshot_1.png file.
#rgcodes below is a script that will capture screenshots with a numeric count indicator per your original post. (tested it on Ubuntu 20.04)
Script contents:
#!/bin/bash
set -uo pipefail
# add source file and line number to xtrace output
# i.e. when running: bash -x ./your_script_name.sh
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
capture_screenshot() {
local output_dir="${1:-/tmp/screenshot}"
local img_name="${2:-Screenshot}"
local img_ext="${3:-png}"
# create output directory (if not already existing)
mkdir -p "${output_dir}"
# get the last image in the sorted ls output
local latest_png=$(tail -n 1 \
<(sort -n -t _ -k 2 \
<(ls ${output_dir}/*.${img_ext} 2> /dev/null)))
# use the latest image to determine img_cnt value for next image
local img_cnt=0
if [[ ${latest_png} =~ _([0-9]+)\.png ]]; then
img_cnt=$((1+${BASH_REMATCH[1]}))
elif [[ ${latest_png} =~ ${img_name}.${img_ext} ]] ; then
img_cnt=1
fi
# build path to output image
local img_path="${output_dir}/${img_name}_${img_cnt}.${img_ext}"
# remove count from output image path if count == 0
if [[ "${img_cnt}" -eq "0" ]] ; then
img_path="${output_dir}/${img_name}.${img_ext}"
fi
xfce4-screenshooter -f -s "${img_path}"
}
capture_screenshot "$#"
The uses the following as defaults, but you can change them to meet your requirements.
output directory for screenshots:
/tmp/screenshot
base screenshot image name:
Screenshot
screenshot file extension:
.png
The script will attempt to create the output directory if it does not already exist (subject to user permission for creation). Below is a sample usage.
Prior to initial script execution, the output directory does not exist:
$ ls screenshot
$
Initial execution (directory is created and Screenshot.png created:
$ ./script.sh
$ ls /tmp/screenshot/
Screenshot.png
Subsequent executions:
$ ./script.sh
$ ls /tmp/screenshot/
Screenshot_1.png Screenshot.png
$ ./script.sh
$ ls /tmp/screenshot/
Screenshot_1.png Screenshot_2.png Screenshot.png
Indeed, as suggested by #j_b in the comments, you should definitely give a try to using a timestamp with the command date +"$format".
FTR, the same idea is implemented here in this project of a gnome-screenshot bash wrapper
(disclaimer: I am the author of this repo).
Example command:
date "+%Y-%m-%d_%H-%M-%S"
↓
2021-07-29_19-13-30
So the overall script could just be something like:
#!/usr/bin/env bash
xfce4-screenshooter -f -s "$HOME/Pictures/Screenshot_$(date "+%Y-%m-%d_%H-%M-%S").png"
(Note that I added missing double-quotes, and modified your shebang, as /usr/bin/env bash is more portable than /bin/bash or /usr/bin/bash.)

Shell or Perl script to rename folders from a text file

I'm trying t look for a way to rename folders in Linux via a shell or perl script based on the first two columns in a .txt file:
Example of text file
rename.txt
current_dir1 new_dir1
current_dir2 new_dir2
current_dir3 new_dir3
...
current_dir50 new_dir50
Then the shell/Perl script would create another file with all the mv commands to run based on what's in the list:
rename_folders.sh or .pl
#!/bin/ksh
mv /home/pete1/current_dir1 /home/pete1/new_dir1
mv /home/pete1/current_dir2 /home/pete1/new_dir2
mv /home/pete1/current_dir3 /home/pete1/new_dir3
........
mv /home/pete1/current_dir2 /home/pete1/new_dir2
#!/usr/bin/env bash
folder_path="/home/pete1/"
while read -r oldname newname; do
# remove echo if happy with result
# if you remove echo it will rename
echo mv "$folder_path/$oldname" "$folder_path/$newname";
done < rename.txt
If you make done < rename.txt to done < rename.txt >somefile.sh it will write all commands which is result of echo

How to pipe files one by one from list into script?

I have a list of files that I need to pipe into a shell script. I can list the files within a directory by using the following:
ls ~/data/2121/*SOMEFILE*
resulting in:
2121.SOMEFILEaa
2121.SOMEFILEab
2121.SOMEFILEac
and so on...
I have another script that performs some processing on a single file (2121.SOMEFILEaa) which I run by using the following command:
bash runscript ../data/2121/2121.SOMEFILEaa
However, I need to make this more efficient by piping individual files from the list of files generated via ls into the script. How can I pipe the results from the ls ~/data/2121/*SOMEFILES* command--file by file--into the runscript script?
Another option
ls ~/data/2121/*SOMEFILE* | xargs -L1 bash runscript
I think you are looking for this:
for file in ~/data/2121/*SOMEFILE*; do
bash runscript "$file"
done
In this way, you're calling bash runscript for each file.
$ cat pipe.sh
#!/bin/bash
## Store data from pipe to variable $PIPE ------#
_read_pipe(){ #
while read -t 10 pipe; do
if [ -n "$pipe" ] ;then
PIPE="$PIPE $pipe" ;fi ;done ;}
## your code -----------------------------------#
_read_pipe #
for kung_foo in $PIPE ;do
echo $kung_foo ;done
$ ls 2121.SOMEFILE* | ./pipe.sh
2121.SOMEFILEaa
2121.SOMEFILEab
2121.SOMEFILEac
and so on...
[ -t ] is for timeout
I hope this helps,
cheers Karim

Resources