reading variable from file becomes text - bash

I must read include-lines from a script. The include-lines are build by variables. When I ls the file based on these variables, its working fine. When I do the same after I have collected the line into my array, the variables behave like somehow invisible changed from $VAR to \$VAR
#!/bin/bash
cd ..
DIR=scratchpad
SCRIPT=$(basename $0)
echo $DIR/$SCRIPT
ls -1 $DIR/$SCRIPT
f_doNothing () {
. $DIR/$SCRIPT
}
while read LINE ; do
if [[ "$LINE" = ". "* ]] ; then
LINE=${LINE/. /}
LINE=${LINE/\#*/}
LINE=${LINE//\"/}
LIBS+=($LINE)
echo ${LIBS[#]}
ls -l ${LIBS[#]}
fi
done < <(cat $DIR/$SCRIPT)
The two last lines of output show the damaged variable and the non-working ls.
$ ./read_include.sh
scratchpad/read_include.sh
scratchpad/read_include.sh
$DIR/$SCRIPT
ls: cannot access $DIR/$SCRIPT: No such file or directory
Can you please help me to understand what I am doing wrong. Thanks!

Related

How to add a character to folder names when using the ls command?

I would like to display a character at the beginning of all folder names when typing the ls command.
So instead of this:
ls
Folder-1 Folder-2 file.txt
It displays this:
ls
📁Folder-1 📁Folder-2 file.txt
Is there a script I can write in my .bash_profile to do this?
You can make a custom script that makes it for you:
#!/bin/bash
lsmod=($(ls)) # Convert ls output to an array
for folder in ${lsmod[#]}; do # Iterate over ls results
if [[ -d $folder ]]; then # If this is a folder then...
echo "=> $folder" # Put your char
else # If not, display normally
echo "$folder"
fi
done
Folders are pointed out with a =>. Hope this helps.
One line approach:
for i in `ls`; do echo $([[ -d "$i" ]] && echo "=> $i" || echo "$i"); done;

Script is not glob-expanding, but works fine when running the culprit as a minimalistic example

I've been trying for hours on this problem, and cannot set it straight.
This minimal script works as it should:
#!/bin/bash
wipe_thumbs=1
if (( wipe_thumbs )); then
src_dir=$1
thumbs="$src_dir"/*/t1*.jpg
echo $thumbs
fi
Invoke with ./script workdir and a lot of filenames starting with t1* in all the sub-dirs of workdir are shown.
When putting the above if-case in the bigger script, the globbing is not executed:
SRC: -- workdir/ --
THUMBS: -- workdir//*/t1*.jpg --
ls: cannot access workdir//*/t1*.jpg: No such file or directory
The only difference with the big script and the minimal script is that the big script has a path-validator and getopts-extractor. This code is immediately above the if-case:
#!/bin/bash
OPTIONS=":ts:d:"
src_dir=""
dest_dir=""
wipe_thumbs=0
while getopts $OPTIONS opt ; do
case "$opt" in
t) wipe_thumbs=1
;;
esac
done
shift $((OPTIND - 1))
src_dir="$1"
dest_dir="${2:-${src_dir%/*}.WORK}"
# Validate source
echo -n "Validating source..."
if [[ -z "$src_dir" ]]; then
echo "Can't do anything without a source-dir."
exit
else
if [[ ! -d "$src_dir" ]]; then
echo "\"$src_dir\" is really not a directory."
exit
fi
fi
echo "done"
# Validate dest
echo -n "Validating destination..."
if [[ ! -d "$dest_dir" ]]; then
mkdir "$dest_dir"
(( $? > 0 )) && exit
else
if [[ ! -w "$dest_dir" ]]; then
echo "Can't write into the specified destination-dir."
exit
fi
fi
echo "done"
# Move out the files into extension-named directories
echo -n "Moving files..."
if (( wipe_thumbs )); then
thumbs="$src_dir"/*/t1*.jpg # not expanded
echo DEBUG THUMBS: -- "$thumbs" --
n_thumbs=$(ls "$thumbs" | wc -l)
rm "$thumbs"
fi
...rest of script, never reached due to error...
Can anyone shed some lights on this? Why is the glob not expanded in the big script, but working fine in the minimalistic test script?
EDIT: Added the complete if-case.
The problem is that wildcards aren't expanded in assignment statements (e.g. thumbs="$src_dir"/*/t1*.jpg), but are expanded when variables are used without double-quotes. Here's an interactive example:
$ src_dir=workdir
$ thumbs="$src_dir"/*/t1*.jpg
$ echo $thumbs # No double-quotes, wildcards will be expanded
workdir/sub1/t1-1.jpg workdir/sub1/t1-2.jpg workdir/sub2/t1-1.jpg workdir/sub2/t1-2.jpg
$ echo "$thumbs" # Double-quotes, wildcards printed literally
workdir/*/t1*.jpg
$ ls $thumbs # No double-quotes, wildcards will be expanded
workdir/sub1/t1-1.jpg workdir/sub2/t1-1.jpg
workdir/sub1/t1-2.jpg workdir/sub2/t1-2.jpg
$ ls "$thumbs" # Double-quotes, wildcards treated as literal parts of filename
ls: workdir/*/t1*.jpg: No such file or directory
...so the quick-n-easy fix is to remove the double-quotes from the ls and rm commands. But this isn't safe, as it'll also cause parsing problems if $src_dir contains any whitespace or wildcard characters (this may not be an issue for you, but I'm used to OS X where spaces in filenames are everywhere, and I've learned to be careful about these things). The best way to do this is to store the list of thumb files as an array:
$ src="work dir"
$ thumbs=("$src_dir"/*/t1*.jpg) # No double-quotes protect $src_dir, but not the wildcard portions
$ echo "${thumbs[#]}" # The "${array[#]}" idiom expands each array element as a separate word
work dir/sub1/t1-1.jpg work dir/sub1/t1-2.jpg work dir/sub2/t1-1.jpg work dir/sub2/t1-2.jpg
$ ls "${thumbs[#]}"
work dir/sub1/t1-1.jpg work dir/sub2/t1-1.jpg
work dir/sub1/t1-2.jpg work dir/sub2/t1-2.jpg
You might also want to set nullglob in case there aren't any matches (so it'll expand to a zero-length array).
In your script, this'd come out something like this:
if (( wipe_thumbs )); then
shopt -s nullglob
thumbs=("$src_dir"/*/t1*.jpg) # expanded as array elements
shopt -u nullglob # back to "normal" to avoid unexpected behavior later
printf 'DEBUG THUMBS: --'
printf ' "%s"' "${thumbs[#]}"
printf ' --\n'
# n_thumbs=$(ls "${thumbs[#]}" | wc -l) # wrong way to do this...
n_thumbs=${#thumbs[#]} # better...
if (( n_thumbs == 0 )); then
echo "No thumb files found" >&2
exit
fi
rm "${thumbs[#]}"
fi

how to write foreach in one line in csh?

Sometimes I use same foreach operation with different target files in csh.
If I can give foreach command in a single line, I could easily substitue the target file names to repeat the process.(I usually use `find . -name ...` for target files)
For the purpose of this question, let's assume I want to cat all the *.txt files.
$ foreach i (*.txt)
foreach? cat $i
foreach? end
I read https://unix.stackexchange.com/questions/32873/running-a-full-foreach-command and tried
alias disp 'echo "foreach i (*.txt)"; echo "cat "\$"i"; echo "end"; | /bin/csh'
when I type disp it gives me Invalid null command.
If I can do it in a single line, I could do !foreach:gs/\.c\>/.h/ (do the same replacing .c with .h).
How can I do it?
This method works for me:
printf 'foreach f ( 1 2 3 )\n echo $f \n end' | tcsh
There is no easy way to do the foreach in one line under tcsh.
However, by using alias, you may get something very close to your question.
alias disp 'foreach i (*.txt)\
cat $i\
end'
You can call disp in your terminal.
If you want to direct the result into a pipe, then echo `disp` | grep foobar.
Sometimes, we need to save the result first, before further processing: set foo=`disp`
For your first question, I don't know how to write this in .cshrc file directly.
But I will use another flexible way to do this.
1: Write a shell script:
/home/user/disp.sh (or any path you want)
#/bin/bash
for line in `find . -name "*.c"`; do
echo ""
cat "$line"
done
Or you only have csh shell:
#/bin/csh
foreach i (*.c)
echo $i
end
Add new alias command to the .cshrc file
alias disp /home/user/disp.sh
Reenter csh shell (or relogin)
csh
For your second question, I did it on the same disp.sh script
This example is written in bash shell script.
#/bin/bash
usage() { echo "Usage: $0 [-d <depth>] [-e <extension>]" 1>&2; exit 1; }
while getopts ":d:e:" o; do
case "${o}" in
d)
d=${OPTARG}
;;
e)
e=${OPTARG}
;;
*)
usage
;;
esac
done
shift $((OPTIND-1))
if [ -z "${d}" ] || [ -z "${e}" ]; then
usage
fi
for line in `find . -maxdepth ${d} -name "*.${e}"`; do
echo ""
cat "$line"
done
You can simply type disp -d 1 -e c to find all .c file in the current path.
(-d means the depth of the file which is started from current path, -e means extension of files)
I tried to find answer but it looks there is no way. Just use bash by simply changing shell.
% bash
$ for f in *txt; do echo $f; done
$ exit
%
Use bash -c:
$ bash -c 'for i in *.txt; do cat $i; done'

Bash script trouble interpretting input

I wrote a bash script that uploads a file on my home server. It gets activated from a folder action script using applescript. The setup is the folder on my desktop is called place_on_server. Its supposed to have an internal file structure exactly like the folder I want to write to: /var/www/media/
usage goes something like this:
if directory etc added to place_on_server: ./upload DIR etc
if directory of directory: etc/movies ./upload DIR etc movies //and so on
if file to place_on_server: ./upload F file.txt
if file in file in place_on_server ./upload F etc file.txt //and so on
for creating a directory its supposed to execute a command like:
ssh root#192.168.1.1<<EOF
cd /var/www/media/wherever
mkdir newdirectory
EOF
and for file placement:
rsync -rsh='ssh -p22' file root#192.168.1.1:/var/www/media/wherever
script:
#!/bin/bash
addr=$(ifconfig -a | ./test)
if ($# -le "1")
then
exit
elif ($1 -eq "DIR")
then
f1="ssh -b root#$addr<<EOF"
list = "cd /var/www/media\n"
if($# -eq "2")
then
list=list+"mkdir $2\nEOF\n"
else
num=2
i=$(($num))
while($num < $#)
do
i=$(($num))
list=list+"mkdir $i\n"
list=list+"cd $i\n"
$num=$num+1
done
fi
echo $list
elif ($1 -eq "F")
then
#list = "cd /var/www/media\n"
f2="rsync -rsh=\'ssh -p22\' "
f3 = "root#$addr:/var/www/media"
if($# -eq "2")
then
f2=f2+$2+" "+f3
else
num=3
i=$(($num))
while($num < $#)
do
i=$(($num))
f2=f2+"/"+$i
$num=$num+1
done
i=$(($num))
f2=f2+$i+" "+$f3
fi
echo $f2
fi
exit
output:
(prompt)$ ./upload2 F SO test.txt
./upload2: line 3: 3: command not found
./upload2: line 6: F: command not found
./upload2: line 25: F: command not found
So as you can see I'm having issues handling input. Its been awhile since I've done bash. And it was never extensive to begin with. Looking for a solution to my problem but also suggestions. Thanks in advance.
For comparisons, use [[ .. ]]. ( .. ) is for running commands in subshells
Don't use -eq for string comparisons, use =.
Don't use < for numerical comparisons, use -lt
To append values, f2="$f2$i $f3"
To add line feeds, use $'\n' outside of double quotes, or a literal linefeed inside of them.
You always need "$" on variables in strings to reference them, otherwise you get the literal string.
You can't use spaces around the = in assignments
You can't use $ before the variable name in assignments
To do arithmetics, use $((..)): result=$((var1+var2))
For indirect reference, such as getting $4 for n=4, use ${!n}
To prevent word splitting removing your line feeds, double quote variables such as in echo "$line"
Consider writing smaller programs and checking that they work before building out.
Here is how I would have written your script (slightly lacking in parameter checking):
#!/bin/bash
addr=$(ifconfig -a | ./test)
if [[ $1 = "DIR" ]]
then
shift
( IFS=/; echo ssh "root#$addr" mkdir -p "/var/www/media/$*"; )
elif [[ $1 = "F" ]]
then
shift
last=$#
file=${!last}
( IFS=/; echo rsync "$file" "root#$addr:/var/www/media/$*" )
else
echo "Unknown command '$1'"
fi
$* gives you all parameters separated by the first character in $IFS, and I used that to build the paths. Here's the output:
$ ./scriptname DIR a b c d
ssh root#somehost mkdir -p /var/www/media/a/b/c/d
$ ./scriptname F a b c d somefile.txt
rsync somefile.txt root#somehost:/var/www/media/a/b/c/d/somefile.txt
Remove the echos to actually execute.
The main problem with your script are the conditional statements, such as
if ($# -le "1")
Despite what this would do in other languages, in Bash this is essentially saying, execute the command line $# -le "1" in a subshell, and use its exit status as condition.
in your case, that expands to 3 -le "1", but the command 3 does not exist, which causes the error message
./upload2: line 3: 3: command not found
The closest valid syntax would be
if [ $# -le 1 ]
That is the main problem, there are other problems detailed and addressed in that other guy's post.
One last thing, when you're assigning value to a variable, e.g.
f3 = "root#$addr:/var/www/media"
don't leave space around the =. The statement above would be interpreted as "run command f3 with = and "root#$addr:/var/www/media" as arguments".

Variable with '-' (minus signals) in Bash

This is so simple yet...
FOLDER='/home/user/.ssh'
SSH="$FOLDER/local-rsync-key.pub"
if [ -f "$SSH" ]; then
...
It looks that Bash considers the '-' as a minus signal and the IF statement always fails... How can I write this variable the right way?
UPDATE:
This is another real example:
I am tring to rename files with "-" in front of the filename, for example: "-0001.jpg"
However, everyime I try to run:
for i in *; do mv "$i" "${i//-/}"; done
or:
for i in *; do mv "$i" "${i#*-}"; done
I got this error:
mv: invalid option -- '0'
Try `mv --help' for more information.
Thanks for any light!
You should not have a $ in front of your SSH assignment, that's only needed when you're using the variable. Without that, it works fine, as in the following transcript:
pax> touch abc-xyz
pax> ll a*
-rw-r--r-- 1 pax paxgrp 0 2011-06-24 05:15 abc-xyz
pax> FOLDER=.
pax> $SSH="$FOLDER/abc-xyz"
bash: =./abc-xyz: No such file or directory
pax> SSH="$FOLDER/abc-xyz"
pax> if [ -f "$SSH" ]
...> then
...> echo yes
...> fi
yes
pax> _
The answer is to use "--" (indicating no more options) after "mv" or "./" before the name of the file (indicating it is about a file). For example:
for i in *; do mv -- "$i" "${i#*-}"; done
or:
for i in *; do mv -- "$i" "./${i#*-}"; done
In bash syntax, when you set a variable just use the variable name:
VAR=value
When you reference the variable, use the $ prefix:
echo $VAR
Your code has a stray dollar sign prefix where you are trying to set the SSH variable. The dashes inside the variable should be no problem.

Resources