how to avoid non printable characters in for each loops (shell scripting) - linux

I wrote a shell script as follows
for i in `readlink -f rec*`:
do
cd $i
pwd
cd ..
pwd
done
The following are the details of corresponding directories:
readlink -f rec*
/home/sandilya/part655gib/recup_dir.1
/home/sandilya/part655gib/recup_dir.2
/home/sandilya/part655gib/recup_dir.3
/home/sandilya/part655gib/recup_dir.4
/home/sandilya/part655gib/recup_dir.5
the script output:
/home/sandilya/part655gib/recup_dir.1
/home/sandilya/part655gib
/home/sandilya/part655gib/recup_dir.2
/home/sandilya/part655gib
/home/sandilya/part655gib/recup_dir.3
/home/sandilya/part655gib
/home/sandilya/part655gib/recup_dir.4
/home/sandilya/part655gib
mvrec.sh: 5: cd: can't cd to /home/sandilya/part655gib/recup_dir.5:
/home/sandilya/part655gib
/home/sandilya
The problem is that last directory is left out.. I was expecting that the last entry is combined with a non printable character. Permissions are all okay with the directories.
Please help me out of this mess. Thanks in advance

The problem is more apparent than it looks. There is no non-printable character.
Let's take a look a cd error message from dash, the shell you're using instead of bash:
$ cd somewhere
dash: 5: cd: can't cd to somewhere
^-- Nothing follows the path
Now look at your error message:
mvrec.sh: 5: cd: can't cd to /home/sandilya/part655gib/recup_dir.5:
Spurious colon ---^
And where does this colon come from?
for i in `readlink -f rec*`:
^--- Right here
Delete it, and it'll work.
(Note that this is not canonical or entirely correct code, and people's suggestions about for f in rec* loops and subshells still apply.)

Related

Linux command and single quote

I thought the single quotes simply reserve the literal value of each character within the quotes and do not interfere with the command result except for those character-escaping situations.
For example, the two commands below returned the same result.
$ ls /home/testuser/tmp1
$ ls '/home/testuser/tmp1'
This command below ran successfully.
$ cp /home/testuser/tmp1/* /home/testuser/tmp2
However, the command below ran into error.
$ cp '/home/testuser/tmp1/*' /home/testuser/tmp2
cp: cannot stat '/home/testuser/tmp1/*': No such file or directory
What did I do wrong here?
* is what you call a character escaping situation. Since you preserved the literal value of *, it lost its meaning of "all files", and cp instead tries to copy a file named literally *.
When you run:
cp /home/testuser/tmp1/* /home/testuser/tmp2
The shell will transparently rewrite it into:
cp /home/testuser/tmp1/bar /home/testuser/tmp1/foo /home/testuser/tmp2
This process is known as "pathname expansion" or "globbing". When you quote the *, this rewrite does not happen.

string manipulation of Directory structure

Scenario: I have a script but no idea where I am in the directory tree, I need to resolve back to the nearest known location UPROC[something]
What I have so far:
I have a script running in a directory for example:
/home/jim/query/UPROCL/test/bob/dircut.sh
now the only constant in this is that the Directory I want will begin with UPROC... maybe not UPROCL but definitely UPROC
So I have written the following:
#!/bin/bash
#Absolute path for this script
SCRIPT=$(readlink -f "$0")
echo $SCRIPT
#Gets Path of script without script name
SCRIPTPATH=$(dirname "$SCRIPT")
echo $SCRIPTPATH
#Cuts everything after UPROC(.* is wildcard)/
CUTDOWN=$(sed 's/\(UPROC.*\/\).*/\1/' <<< $SCRIPTPATH)
echo $CUTDOWN
The only problem is that it output is:
/home/jim/query/UPROCL/test/bob/dircut.sh
/home/jim/query/UPROCL/test/bob
/home/jim/query/UPROCL/test/
Can some tell me what is wrong with my sed command as it is not cutting down to
/home/jim/query/UPROCL/
Because * is greedy. You want to be more selective about what characters are allowed following "UPROC" -- any non-slash
Not
sed 's/\(UPROC.*\/\).*/\1/'
but
sed -r 's,(UPROC[^/]*/).*,\1,'
Using different delimiters for the s/// command reduces the "leaning toothpick" problem.
Because the .* in the () is matching to the / at the end of test/.
You need [^/]* instead of . to not match any slashes.
When you want to know in which directory you are, why don't use pwd?
One thing which might be useful: the command pwd shows the value of the environment variable PWD (uppercase). In case you want to use the current directory as a value, you might use this.

I can't get my bash script to run

This is the script that I used to that will not run, but I am hoping someone can help me figure out what the issue is. I am new to unix
#!/bin/bash
# cat copyit
# copies files
numofargs=$#
listoffiles=
listofcopy=
# Capture all of the arguments passed to the command, store all of the arguments, except
# for the last (the destination)
while [ "$#" -gt 1 ]
do
listoffiles="$listoffiles $1"
shift
done
destination="$1"
# If there are less than two arguments that are entered, or if there are more than two
# arguments, and the last argument is not a valid directory, then display an
# error message
if [ "$numofargs" -lt 2 -o "$numofargs" -gt 2 -a ! -d "$destination" ]
then
echo "Usage: copyit sourcefile destinationfile"
echo" copyit sourcefile(s) directory"
exit 1
fi
# look at each sourcefile
for fromfile in $listoffiles
do
# see if destination file is a directory
if [ -d "$destination" ]
then
destfile="$destination/`basename $fromfile`"
else
destfile="$destination"
fi
# Add the file to the copy list if the file does not already exist, or it
# the user
# says that the file can be overwritten
if [ -f "$destfile" ]
then
echo "$destfile already exist; overwrite it? (yes/no)? \c"
read ans
if [ "$ans" = yes ]
then
listofcopy="$listofcopy $fromfile"
fi
else
listofcopy="$listofcopy $fromfile"
fi
done
# If there is something to copy - copy it
if [ -n "$listofcopy" ]
then
mv $listofcopy $destination
fi
This is what I got and it seems that the script didn't execute all though I did invoke it. I am hoping that someone can help me
[taniamack#localhost ~]$ chmod 555 tryto.txt
[taniamack#localhost ~]$ tryto.txt
bash: tryto.txt: command not found...
[taniamack#localhost ~]$ ./tryto.txt
./tryto.txt: line 7: $'\r': command not found
./tryto.txt: line 11: $'\r': command not found
./tryto.txt: line 16: $'\r': command not found
./tryto.txt: line 43: syntax error near unexpected token `$'do\r''
'/tryto.txt: line 43: `do
Looks like your file contains Windows new line formatting: "\r\n". On Unix, a new line is just "\n". You can use dos2unix (apt-get install dos2unix), to convert your files.
Also have a look at the chmod manual (man chmod).
Most of the time i just use chmod +x ./my_file to give execution rights
I see a few issues. First of all, a mode of 555 means that no one can write to the file. You probably want chmod 755. Second of all, you need to add the current directory to your $PATH variable. In Windows, you also have a %PATH%, but by default the current directory . is always in %PATH%, but in Unix, adding the current directory is highly discouraged because of security concerns. The standard is to put your scripts under the $HOME/bin directory and make that directory the last entry in your $PATH.
First of all: Indent correctly. When you enter a loop or an if statement, indent the lines by four characters (that's the standard). It makes it much easier to read your program.
Another issue is your line endings. It looks like some of the lines have a Windows line ending on them while most others have a Unix/Linux/Mac line ending. Windows ends each line with two characters - Carriage Return and Linefeed while Unix/Linux/Mac end each line with just a Linefeed. The \r is used to represent the Carriage Return character. Use a program editor like vim or gedit. A good program editor will make sure that your line endings are consistent and correct.

cat using relative paths on mac

I am trying to cat 4 files one directory down to a new file, also one directory down:
cat ./dira/file.txt ./dirb/file.txt ./dirc/file.txt ./dird/file.txt > ./dire/file.txt
I can get this to work from the Terminal, but not in the following:
for i in `ls -d prefix*`
do
cd $i
pwd
  cat ./dira/file.txt ./dirb/file.txt ./dirc/file.txt ./dird/file.txt > ./dire/file.txt
done
where pwd prints the correct directory. I get the error: -bash:  : command not found.
There must be a non-breaking space at the start of one of the lines in your file (easily done by typing option-space by accident during editing). The shell would consider that to be a word and try to run the non-breaking space as a command; this produces the "bash: : command not found" error that you see.

linux batch rename directories and strip # character from name

i have a directory with a lot of subdirectories with a # infront of them:
#adhasdk
#ad18237
I want to rename them all and remove the # caracter
I tried to do:
rename -n `s/#//g` *
but didn't seem to work.
-bash: s/#//g: No such file or directory
Any ideas on this.
Thanks
Just use
$ rename 's/^#//' *
use -n just to check that what you think it would happen really happens.
In you example you have the clue about the wrong quotes used (backticks) in the error message
-bash: s/#//g: No such file or directory
bash is trying to execute a command named s/#//g.
No that using g (global) and not anchoring the regular expression you will replace any #, not just the one in the first position.
I don't know whether it's just a typo when you typed it here, but that "rename" command should work if:
you leave off the "-n" and
you quote the substitution with regular single-quotes and not back-quotes
The "-n" tells it to not really do anything. The back-quotes are just wrong (they mean something but not what you want here).
The problem is that you use backticks (`). You should use normal quotes:
rename -n 's/#//g' *
for DIR in \#*/
do
echo mv "$DIR" "${DIR/#\#/}"
done
I had to rename all folders inside a given folder. Each folder name had some text inside round braces. The following command removed the round braces from all folder names:
rename 's/(.+)//' *
Some distros doesn't support regexp in rename. You have to install prename. Even more, sometimes you can't install prename and you have to install gprename to have binary prename.
If you have 'prename' then just change backtick character " ` " to single quote and everything should work.
So the solution should be:
prename -n 's/#//g' *
or
prename -n 'y/#//' *

Resources