Remove eval in an existing code - sh

I am working on an existing shell script code which has eval. I feel like that eval is unnecessary here and wanted to remove to avoid Injection.
Could you please check the code and advise why there is an eval in the code.
FILE_PATH=`echo $1 | awk '{ print $10 }' | cut -f2 -d'"'
FILE_PATH=`(eval "echo ${FILE_PATH}")`

if $1 is something like that ---"~/tttttttt.txt.
FILE_PATH will be ~/tttttttt.txt without eval.
but with eval;
FILE_PATH will be /home/user/tttttttt.txt
#!/bin/bash
path='-----"~/tttttttt.txt'
FILE_PATH=`echo $path | awk '{ print $1 }' | cut -f2 -d'"'`
echo "${FILE_PATH}"
ls -lart ${FILE_PATH}
FILE_PATH=`(eval "echo ${FILE_PATH}")`
echo $FILE_PATH
ls -lart ${FILE_PATH}
if run above script, output:
~/tttttttt.txt
ls: cannot access ~/tttttttt.txt: No such file or directory
/home/user/tttttttt.txt
-rw-rw-r-- 1 user user 0 Aug 26 15:54 /home/user/tttttttt.txt

Related

Bash or Python efficient substring matching and filtering

I have a set of filenames in a directory, some of which are likely to have identical substrings but not known in advance. This is a sorting exercise. I want to move the files with the maximum substring ordered letter match together in a subdirectory named with that number of letters and progress to the minimum match until no matches of 2 or more letters remain. Ignore extensions. Case insensitive. Ignore special characters.
Example.
AfricanElephant.jpg
elephant.jpg
grant.png
ant.png
el_gordo.tif
snowbell.png
Starting from maximum length matches to minimum length matches will result in:
./8/AfricanElephant.jpg and ./8/elephant.jpg
./3/grant.png and ./3/ant.png
./2/snowbell.png and ./2/el_gordo.tif
Completely lost on an efficient bash or python way to do what seems a complex sort.
I found some awk code which is almost there:
{
count=0
while ( match($0,/elephant/) ) {
count++
$0=substr($0,RSTART+1)
}
print count
}
where temp.txt contains a list of the files and is invoked as eg
awk -f test_match.awk temp.txt
Drawback is that a) this is hardwired to look for "elephant" as a string (I don't know how to make it take an input string (rather than file) and an input test string to count against, and
b) I really just want to call a bash function to do the sort as specified
If I had this I could wrap some bash script around this core awk to make it work.
function longest_common_substrings () {
shopt -s nocasematch
for file1 in * ; do for file in * ; do \
if [[ -f "$file1" ]]; then
if [[ -f "$file" ]]; then
base1=$(basename "$file" | cut -d. -f1)
base2=$(basename "$file1" | cut -d. -f1)
if [[ "$file" == "$file1" ]]; then
echo -n ""
else
echo -n "$file $file1 " ; $HOME/Scripts/longest_common_substring.sh "$base1" "$base2" | tr -d '\n' | wc -c | awk '{$1=$1;print}' ;
fi
fi
fi
done ;
done | sort -r -k3 | awk '{ print $1, $3 }' > /tmp/filesort_substring.txt
while IFS= read -r line; do \
file_to_move=$(echo "$line" | awk '{ print $1 }') ;
directory_to_move_to=$(echo "$line" | awk '{ print $2 }') ;
if [[ -f "$file_to_move" ]]; then
mkdir -p "$directory_to_move_to"
\gmv -b "$file_to_move" "$directory_to_move_to"
fi
done < /tmp/filesort_substring.txt
shopt -u nocasematch
where $HOME/Scripts/longest_common_substring.sh is
#!/bin/bash
shopt -s nocasematch
if ((${#1}>${#2})); then
long=$1 short=$2
else
long=$2 short=$1
fi
lshort=${#short}
score=0
for ((i=0;i<lshort-score;++i)); do
for ((l=score+1;l<=lshort-i;++l)); do
sub=${short:i:l}
[[ $long != *$sub* ]] && break
subfound=$sub score=$l
done
done
if ((score)); then
echo "$subfound"
fi
shopt -u nocasematch
Kudos to the original solution for computing the match in the script which I found elsewhere in this site

Split results of du command by new line

I have got a list of the top 20 files/folders that are taking the most amount of room on my hard drive. I would like to separate them into size path/to/file. Below is what I have done so far.
I am using: var=$(du -a -g /folder/ | sort -n -r | head -n 20). It returns the following:
120 /path/to/file
115 /path/to/another/file
110 /file/path/
etc.
I have tried the following code to split it up into single lines.
for i in $(echo $var | sed "s/\n/ /g")
do
echo "$i"
done
The result I would like is as follows:
120 /path/to/file,
115 /path/to/another/file,
110 /file/path/,
etc.
This however is the result I am getting:
120,
/path/to/file,
115,
/path/to/another/file,
110,
/file/path/,
etc.
I think awk will be easier, can be combined with a pipe to the original command:
du -a -g /folder/ | sort -n -r | head -n 20 | awk '{ print $1, $2 "," }'
If you can not create a single pipe, and have to use $var
echo "$var" | awk '{ print $1, $2 "," }'

Resolve name by inode in current direcory

How can I resolve the name by the given inode in the current directory in the following script that prints all filenames of symlinks pointing to a specified file that is passed as an argument to the script. The list should be sorted by ctime.
#!/usr/bin/ksh
IFS="`printf '\n\t'`"
USAGE="usage: symlink.sh <file>"
get_ctime() {
perl -se 'use File::stat; $file=lstat($filename); print $file->ctime' -- -filename="$1"
}
stat_inode() {
perl -se 'use File::stat; $file=stat($filename); if (defined $file) { print $file->ino; }' -- -filename="$1"
}
lstat_inode() {
perl -se 'use File::stat; $file=lstat($filename); if (defined $file) { print $file->ino; }' -- -filename="$1"
}
if [ $# -eq 0 ]; then
echo "$USAGE"
exit 1
fi
FILE_NAME="$1"
FILE_INODE=$(stat_inode "$FILE_NAME")
if [ ! -e "$FILE_NAME" ]; then
echo "no such file \"$FILE_NAME\""
exit 1
fi
for LINK in ./* ./.[!.]* ;do
if [ -L "$LINK" ]; then
TARGET_INODE=$(stat_inode "$LINK")
if [ ! -z "$TARGET_INODE" ]; then
if [ "$FILE_INODE" -eq "$TARGET_INODE" ]; then
echo $(get_ctime "$LINK") $(lstat_inode "$LINK");
fi
fi
fi
done | sort -nk1 | awk '{print $2}'
Basically, I'd like to pipe awk to some kind of lookup function like this: | awk ' ' | lookup
I'd really appreciate if someone suggested a more elegant way to accomplish the task.
OS: SunOS 5.10
Shell: KSH
Something like this?
$ find . -maxdepth 1 -inum 2883399
./.jshintrc
$
or:
$ echo 2883399 | xargs -IX find . -maxdepth 1 -inum X
./.jshintrc
$

NetworkManager dispatcher script

scripts in /etc/NetworkManager/dispatcher.d will got exec and parameters will be passed to the scripts by NetworkManager.
One of my laptop BIOS is malfunctioning, I have to manually sync the time, and do system upgrade BTW. I am working with a script to automate this task.
Here's the script:
#!/bin/sh
IF=$1
STATUS=$2
if [ "$STATUS"x != 'up'x -o "$(date +%Y)" -gt "2012" ] ;then
exit
fi
logger "==$0=="
wait_for_process(){
PNAME=$1
PID=`pgrep $PNAME`
while [ -z "$PID" ];do
logger "waiting $1 running for another 3 sec.."
sleep 3;
PID=`pgrep $PNAME`
done
logger "$1 is running!"
}
wait_for_process nm-applet
wait_for_process lxpanel
export DISPLAY=$(echo $DISPLAY | cut -c -2)
if [ -z $DISPLAY ];then
export DISPLAY=:0
fi
#below cmd will yield null string for $user
user=$(who | grep "$DISPLAY" | awk '{print $1}' | tail -n1)
#so I have to hardcode the user name:(
user=xxx
export XAUTHORITY="/home/$user/.Xauthority"
logger "Display $DISPLAY user $user"
su $user -c "xterm -e 'sudo /usr/bin/ntpd -qdg && sudo yaourt -Syua' &" || logger "cannot run xterm"
(the script is invoked before x window, run as root)
user=$(who | grep "$DISPLAY" | awk '{print $1}' | tail -n1) cannot find the login user name. But it works in xterm.
Can someone help?
I am using archlinux i686 + openbox + lxpanel
edit:
I want to find the real login user name, while the script is run by root.
Are you looking for the name of the user running the script? How about:
user=$( id -un )

extract number from string

I have a string ABCD20110420.txt and I want to extract the date out of it. Expected 2011-04-20
I can use replace to remove the text part, but how do I insert the "-" ?
# echo "ABCD20110420.txt" | replace 'ABCD' '' | replace '.txt' ''
20110420
echo "ABCD20110420.txt" | sed -e 's/ABCD//' -e 's/.txt//' -e 's/\(....\)\(..\)\(..\)/\1-\2-\3/'
Read: sed FAQ
Just use the shell (bash)
$> file=ABCD20110420.txt
$> echo "${file//[^0-9]/}"
20110420
$> file="${file//[^0-9]/}"
$> echo $file
20110420
$> echo ${file:0:4}-${file:4:2}-${file:6:2}
2011-04-20
The above is applicable to files like your sample. If you have files like A1BCD20110420.txt, then will not work.
For that case,
$> file=A1BCD20110420.txt
$> echo ${file%.*} #get rid of .txt
A1BCD20110420
$> file=${file%.*}
$> echo "2011${file#*2011}"
20110420
Or you can use regular expression (Bash 3.2+)
$> file=ABCD20110420.txt
$> [[ $file =~ ^.*(2011)([0-9][0-9])([0-9][0-9])\.*$ ]]
$> echo ${BASH_REMATCH[1]}
2011
$> echo ${BASH_REMATCH[2]}
04
$> echo ${BASH_REMATCH[3]}
20
echo "ABCD20110420.txt" | sed -r 's/.+([0-9]{4})([0-9]{2})([0-9]{2}).+/\1-\2-\3/'
$ file=ABCD20110420.txt
$ echo "$file" | sed -e 's/^[A-Za-z]*\([0-9][0-9][0-9][0-9]\)\([0-9][0-9]\)\([0-9][0-9]\)\.txt$/\1-\2-\3/'
This only requires a single call to sed.
echo "ABCD20110420.txt" | sed -r 's/.{4}(.{4})(.{2})(.{2}).txt/\1-\2-\3/'