Readlink - How to crop full path? - bash

I use readlink to find a file's full path:
cek=$(readlink -f "$1")
mkdir -p "$ydk$cek"
mv "$1" "$ydk/$cek/$ydkfile"
But readlink -f "$1" gives me the full path. How can I crop the full path?
For example:
/home/test/test/2014/10/13/log.file
But I need just
/test/2014/10/13/
How can I do it?
Judging from multiple comments:
The output should be the last four directory components of the full path returned by readlink.
Given:
full_path=/home/some/where/hidden/test/2014/08/29/sparefile.log
the output should be:
test/2014/08/29
(Don't build any assumption about today's date into the path trimming code.)

If you need the last four directory components of the full path, and if you don't have newlines in the full path, and if you have GNU grep or BSD (Mac OS X) grep with support for -o (output only the matched material) then this gives the required result:
$ cek="/home/test/test/2014/10/13/log.file"
$ echo "${cek%/*}"
/home/test/test/2014/10/13
$ echo "${cek%/*}" | grep -o -E -e '(/[^/]+){4}$'
/test/2014/10/13
$ full_path=/home/some/where/hidden/test/2014/08/29/sparefile.log
$ echo "${full_path%/*}" | grep -o -E -e '(/[^/]+){4}$'
/test/2014/08/29
$
I need path starting /201[0-9]:
/home/bla/bla2/bla3/2014/01/13/13… ⟶ /2014/01/13/13….
So, you need to use grep -o again, starting with the year pattern:
echo "${fullpath%/*}" | grep -o -e '/201[0-9]/.*$'
This is much simpler; you don't even need extended regular expressions for this!
If you need the path element before the year too, then you need:
echo "{fullpath%/*}" | grep -o -e '/[^/][^/]*/201[0-9]/.*$'

Do you really need to remove "/home" ?
cek="/home/test/test/2014/10/13/log.file"
dir=$(dirname "$cek")
echo "${dir#/home}"
/test/test/2014/10/13
Just last 4 directory components:
last4dirs() {
local IFS=/
local -a components=($1)
local l=${#components[#]}
echo "${components[*]:l-5:4}"
}
last4dirs /home/some/where/hidden/test/2014/08/29/sparefile.log
test/2014/08/29

Related

Linux compilation | Invalid entrypoint

I am compiling a linux kernel using mipsel toolchain.
Everything works fine except at the very last point which states invalid entry point:
sh: 0: Can't open /arch/mips/boot/tools/entry
rm -f arch/mips/boot/vmlinux.bin.gz
gzip -9 arch/mips/boot/vmlinux.bin
mkimage -A mips -O linux -T kernel -C gzip \
-a 0x80010000 -e \
-n 'Linux-2.6.31.3-g29b45174-dirty' \
-d arch/mips/boot/vmlinux.bin.gz arch/mips/boot/uImage
mkimage: invalid entry point -n
Now it mentioned sh: 0: Can't open /arch/mips/boot/tools/entry
So I checked that file and it has following:
#!/bin/sh
# grab the kernel_entry address from the vmlinux elf image
entry=`$1 $2 | grep kernel_entry`
fs=`echo $entry | grep ffffffff` # check toolchain output
if [ -n "$fs" ]; then
echo "0x"`$1 $2 | grep kernel_entry | cut -c9- | awk '{print $1}'`
else
echo "0x"`$1 $2 | grep kernel_entry | cut -c1- | awk '{print $1}'`
fi
Now i understand something is generating the kernel entry point, but that generated entry point is invalid.
Question: What exactly generates kernel entry point and what potentially could be done to fix the issue?
The problem can be reproduced through following steps:
Compilation instructions:
$ cd
$ wget https://github.com/steward-fu/a320/releases/download/v1.0/mipsel-4.1.2-nopic.tar.bz2
$ tar xvf mipsel-4.1.2-nopic.tar.bz2
$ sudo mv mipsel-4.1.2-nopic /opt/rg300
$ export PATH=$PATH:/opt/rg300/bin
$ git clone https://github.com/rs-97-cfw/rs97-linux-kernel-NoNDA rg300_kernel
$ cd rg300_kernel
$ ARCH=mips make uImage
This looks like a path issue caused by improper value of an environment variable.
The error message sh: 0: Can't open /arch/mips/boot/tools/entry is a full path relative to /, i.e. the root directory, instead of correctly specifying where your kernel source is actually stored, e.g. /home/your_username/rg300_kernel/arch/mips/boot/tools/entry.
Question: What exactly generates kernel entry point and what potentially could be done to fix the issue?
The issue is not the script itself, but rather how the script is invoked.
The directory path to where your kernel source resides is incorrectly specified.
Because the script is never found and executed, there is no value provided for the -e option for specifying the entry point.
Consequently the mkimage utility (incorrectly) complains of an "invalid entry point", but the actual problem is that no value was obtainable because the script was never located & executed.
The salient text for specifying the path of the script is:
$(KBUILD_SRC)/$(obj)/tools/entry
Your build output indicates that the obj environment variable is correctly set to arch/mips/boot.
But KBUILD_SRC seems to be incorrectly set to just / (the root directory) or is blank (???!!!) or is undefined, rather than something like /home/your_username/rg300_kernel or whatever the correct path is.
For a workaround you could try replacing variable KBUILD_SRC with srctree in arch/mips/boot/Makefile:
uImage: $(VMLINUX) vmlinux.bin
rm -f $(obj)/vmlinux.bin.gz
gzip -9 $(obj)/vmlinux.bin
mkimage -A mips -O linux -T kernel -C gzip \
- -a $(LOADADDR) -e $(shell sh $(KBUILD_SRC)/$(obj)/tools/entry $(NM) $(VMLINUX) ) \
+ -a $(LOADADDR) -e $(shell sh $(srctree)/$(obj)/tools/entry $(NM) $(VMLINUX) ) \
-n 'Linux-$(KERNELRELEASE)' \
-d $(obj)/vmlinux.bin.gz $(obj)/uImage
#echo ' Kernel: arch/mips/boot/$# is ready'
Variable srctree appears to be derived from KBUILD_SRC (in the top-level kernel Makefile), and using it as a substitution is really a WAG for a workaround.
Perhaps somewhere KBUILD_SRC is getting clobbered or not exported, but makefiles (and scripts) is not my expertise so I am unable to explain the underlying cause.

How to oneline two variables via echo?

I try to search for files and seperate path and version as variable because each will be needed later for creating a directory and to unzip a .jar in desired path.
file=$(find /home/user/Documents/test/ -path *.jar)
version=$(echo "$file" | grep -P -o '[0-9].[0-9].[0-9].[0-9]')
path=$(echo "$file" | sed 's/\(.*\)[/].*/\1/')
newpath=$(echo "${path}/${version}")
echo "$newpath"
result
> /home/user/Documents/test/gb0500
> /home/user/Documents/test/gb0500 /home/user/Documents/test/gb0500
> /home/user/Documents/test /home/user/Documents/test/1.3.2.0
> 1.3.2.1
> 1.3.2.2
> 1.2.0.0
> 1.3.0.0
It's hilarious that it's only working at one line.
what else I tried:
file=$(find /home/v990549/Dokumente/test/ -path *.jar)
version=$(grep -P -o '[0-9].[0-9].[0-9].[0-9]')
path=$(sed 's/\(.*\)[/].*/\1/')
while read $file
do
echo "$path$version"
done
I have no experience in scripting. Thats what I figured out some days ago. I am just practicing and trying to make life easier.
find output:
/home/user/Documents/test/gb0500/gb0500-koetlin-log4j2-web-1.3.2.0-javadoc.jar
/home/user/Documents/test/gb0500/gb0500-koetlin-log4j2-web-1.3.2.1-javadoc.jar
/home/user/Documents/test/gb0500/gb0500-koetlin-log4j2-web-1.3.2.2-javadoc.jar
/home/user/Documents/test/gb0500-co-log4j2-web-1.2.0.0-javadoc.jar
/home/user/Documents/test/gb0500-commons-log4j2-web-1.3.0.0-javadoc.jar
As the both variables version and path are newline-separated, how about:
file=$(find /home/user/Documents/test/ -path *.jar)
version=$(echo "$file" | grep -P -o '[0-9].[0-9].[0-9].[0-9]')
path=$(echo "$file" | sed 's/\(.*\)[/].*/\1/')
paste -d "/" <(echo "$path") <(echo "$version")
Result:
/home/user/Documents/test/gb0500/1.3.2.0
/home/user/Documents/test/gb0500/1.3.2.1
/home/user/Documents/test/gb0500/1.3.2.2
/home/user/Documents/test/1.2.0.0
/home/user/Documents/test/1.3.0.0
BTW I do not recommend to store multiple filenames in a single variable
as a newline-separated variable due to several reasons:
Filenames may contain a newline character.
It is not easy to manipulate the values of each line.
For instance you could simply say
the third line as path=${file%/*} if file contains just one.
Hope this helps.

Modify a path stored in a bash script variable

I have a variable f in a bash script
f=/path/to/a/file.jpg
I'm using the variable as an input argument to a program that requires and input and an output path.
For example the program's usage would look like this
./myprogram -i inputFilePath -o outputFilePath
using my variable, I'm trying to maintain the same basename, change the extension, and put the output file into a sub directory. For example
./myprogram -i /path/to/a/file.jpg -o /path/to/a/new/file.tiff
I'm trying to do that by doing this
./myprogram -i "$f" -o "${f%.jpg}.tiff"
of course this keeps the basename, changes the extension, but doesn't put the file into the new subdirectory.
How can I modify f to to change /path/to/a/file.jpg into /path/to/a/new/file.tiff?
Actually you can do this in several ways:
Using sed as pointed out by #anubhava
Using dirname and basename:
./myprogram -i "$f" -o "$(dirname -- "$f")/new/$(basename -- "$f" .jpg).tiff"
Using only Bash:
./myprogram -i "$f" -o "${f%/*}/new/$(b=${f##*/}; echo -n ${b%.jpg}.tiff)"
Note that unlike the second solution (using dirname/basename) that is more robust, the third solution (in pure Bash) won't work if "$f" does not contain any slash:
$ dirname "file.jpg"
.
$ f="file.jpg"; echo "${f%/*}"
file.jpg
You may use this sed:
s='/path/to/a/file.jpg'
sed -E 's~(.*/)([^.]+)\.jpg$~\1new/\2.tiff~' <<< "$s"
/path/to/a/new/file.tiff
If you're on a system that supports the basename and dirnamecommands you could use a simple wrapper function eg:
$ type newSubDir
newSubDir is a function
newSubDir ()
{
oldPath=$(dirname "${1}");
fileName=$(basename "${1}");
newPath="${oldPath}/${2}/${fileName}";
echo "${newPath}"
}
$ newSubDir /path/to/a/file.jpg new
/path/to/a/new/file.jpg
If your system doesn't have those, you can accomplish the same thing using string manipulation:
$ file="/path/to/a/file.jpg"
$ echo "${file%/*}"
/path/to/a
$ echo "${file##*/}"
file.jpg

changing the name of many files by increasing the number

I want to change the file names from a terminal. I have many files, so I cannot change all of them one by one.
a20170606_1257.txt -> a20170606_1300.txt
a20170606_1258.txt -> a20170606_1301.txt
I am only able to change it by:
rename 57.txt 00.txt *57.txt
but it is not enough.
Just playing with parameter expansion to extract the longest and shortest strings of type ${str##*} and ${str%%*}
offset=43
for file in *.txt; do
[ -f "$file" ] || continue
woe="${file%%.*}"; ext="${file##*.}"
num="${woe##*_}"
echo "$file" "${woe%%_*}_$((num+offset)).${ext}"
done
Once you have it working, remove the echo line and replace it with mv -v. Change the offset variable as you wish, depending on where you want to start your re-named files from.
Perl e flag to rescue
rename -n -v 's/(?<=_)(\d+)/$1+43/e' *.txt
test
dir $ ls | cat -n
1 a20170606_1257.txt
2 a20170606_1258.txt
dir $
dir $
dir $ rename -n -v 's/(?<=_)(\d+)/$1+43/e' *.txt
rename(a20170606_1257.txt, a20170606_1300.txt)
rename(a20170606_1258.txt, a20170606_1301.txt)
dir $
dir $ rename -v 's/(?<=_)(\d+)/$1+43/e' *.txt
a20170606_1257.txt renamed as a20170606_1300.txt
a20170606_1258.txt renamed as a20170606_1301.txt
dir $
dir $ ls | cat -n
1 a20170606_1300.txt
2 a20170606_1301.txt
dir $
rename --help:
Usage:
rename [ -h|-m|-V ] [ -v ] [ -n ] [ -f ] [ -e|-E *perlexpr*]*|*perlexpr*
[ *files* ]
Options:
-v, -verbose
Verbose: print names of files successfully renamed.
-n, -nono
No action: print names of files to be renamed, but don't rename.
-f, -force
Over write: allow existing files to be over-written.
-h, -help
Help: print SYNOPSIS and OPTIONS.
-m, -man
Manual: print manual page.
-V, -version
Version: show version number.
-e Expression: code to act on files name.
May be repeated to build up code (like "perl -e"). If no -e, the
first argument is used as code.
-E Statement: code to act on files name, as -e but terminated by

Can I get the absolute path to the current script in KornShell?

Is it possible to find out the full path to the script that is currently executing in KornShell (ksh)?
i.e. if my script is in /opt/scripts/myscript.ksh, can I programmatically inside that script discover /opt/scripts/myscript.ksh ?
Thanks,
You could use:
## __SCRIPTNAME - name of the script without the path
##
typeset -r __SCRIPTNAME="${0##*/}"
## __SCRIPTDIR - path of the script (as entered by the user!)
##
__SCRIPTDIR="${0%/*}"
## __REAL_SCRIPTDIR - path of the script (real path, maybe a link)
##
__REAL_SCRIPTDIR=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )
In korn shell, all of these $0 solutions fail if you are sourcing in the script in question. The correct way to get what you want is to use $_
$ cat bar
echo dollar under is $_
echo dollar zero is $0
$ ./bar
dollar under is ./bar
dollar zero is ./bar
$ . ./bar
dollar under is bar
dollar zero is -ksh
Notice the last line there? Use $_. At least in Korn. YMMV in bash, csh, et al..
Well it took me a while but this one is so simple it screams.
_SCRIPTDIR=$(cd $(dirname $0);echo $PWD)
since the CD operates in the spawned shell with $() it doesn't affect the current script.
How the script was called is stored in the variable $0. You can use readlink to get the absolute file name:
readlink -f "$0"
The variable $RPATH contains the relative path to the real file or the real path for a real file.
CURPATH=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )
CURLOC=$CURPATH/`basename $0`
if [ `ls -dl $CURLOC |grep -c "^l" 2>/dev/null` -ne 0 ];then
ROFFSET=`ls -ld $CURLOC|cut -d ">" -f2 2>/dev/null`
RPATH=`ls -ld $CURLOC/$ROFFSET 2>/dev/null`
else
RPATH=$CURLOC
fi
echo $RPATH
This is what I did:
if [[ $0 != "/"* ]]; then
DIR=`pwd`/`dirname $0`
else
DIR=`dirname $0`
fi
readlink -f would be the best if it was portable, because it resolves every links found for both directories and files.
On mac os x there is no readlink -f (except maybe via macports), so you can only use readlink to get the destination of a specific symbolic link file.
The $(cd -P ... pwd -P) technique is nice but only works to resolve links for directories leading to the script, it doesn't work if the script itself is a symlink
Also, one case that wasn't mentioned : when you launch a script by passing it as an argument to a shell (/bin/sh /path/to/myscript.sh), $0 is not usable in this case
I took a look to mysql "binaries", many of them are actually shell scripts ; and now i understand why they ask for a --basedir option or need to be launched from a specific working directory ; this is because there is no good solution to locate the targeted script
This works also, although it won't give the "true" path if it's a link. It's simpler, but less exact.
SCRIPT_PATH="$(whence ${0})"
Try which command.
which scriptname
will give you the full qualified name of the script along with its absolute path
I upgraded the Edward Staudt's answer, to be able to deal with absolute-path symbolic links, and with chains of links too.
DZERO=$0
while true; do
echo "Trying to find real dir for script $DZERO"
CPATH=$( cd -P -- "$(dirname -- "$(command -v -- "$DZERO")")" && pwd -P )
CFILE=$CPATH/`basename $DZERO`
if [ `ls -dl $CFILE | grep -c "^l" 2>/dev/null` -eq 0 ];then
break
fi
LNKTO=`ls -ld $CFILE | cut -d ">" -f2 | tr -d " " 2>/dev/null`
DZERO=`cd $CPATH ; command -v $LNKTO`
done
Ugly, but works...
After run this, the path is $CPATH and the file is $CFILE
Try using this:
dir = $(dirname $0)
Using $_ provides the last command.
>source my_script
Works if I issue the command twice:
>source my_script
>source my_script
If I use a different sequence of commands:
>who
>source my_script
The $_ variable returns "who"

Resources