bash - moving files based on information extracted from filename - linux

Trying to create a script that will take a tv show video file and move it into the correct folder in my TV directory hierarchy.
ie. example filenames:
archer.2009.s01e01.publichd.mkv
archer.s05e10.dimension.mkv
I would like these moved to: Television/Archer/Season 1/ and Television/Archer/Season 5/ respectively and create them if they don't already exist.
Here's what I have so far, right now it's only sorting based on season for one particular show, Archer. Planning to expand that once I get it working:
#!/bin/bash
for season in 01 02 03 04 05 #06 07 08 09 10 11 12 13 14 15 16
do
echo 'Season ' $season
#find -iname *archer*s$season\*.*
var=$(find -iname *archer*s$season\*.*)
var=$(echo $var | cut -c 3-)
echo $var
var2=$(sed "s/0//" <<< $season)
var3=$(echo "Season $var2/")
echo $var3
if [[ -z "$var" ]]
then
:
else
mkdir -p /home/adam/Downloads/Television/Archer/$var3;
mv "./$var" "/home/adam/Downloads/Television/Archer/$var3"
fi
done
I'm having problems creating/moving the files to the $var3 directory variable. It does have a space in the name which I know is giving me the problems. I already have a large library of shows in this format though so I'm rather not change it.
Any help would be appreciated - I know the script is VERY primitive, I'm just piecing it together as I go.

You need to put quotes around var3 when using it.
mkdir "$var3"

Related

How to take out the numerical values from a list of numbers and words in linux

I'm a newbie in Linux. I was trying to make a nested if loop inside a for loop and found difficulties taking out the numeric values from a list. My code is
#!/bin/bash
t=`ls ListOfthings`
for i in $t
do
if "$i" -eq "$i"
then
echo hi (for example)
fi
done
when I execute this, it said syntax error unexpected token 'done'. I think its some problems related to the "$i" -eq "$i"? Is this the right way to identify the numerical items?
For example, from a list [123 23 63 a.txt b.txt c.sh], 123, 23 and 63 are needed.
Try using regex. For example: egrep '^[0-9]+$' list
Or if you're using ls: ls | egrep '^[0-9]+$'

How to chain 'mimetype -b' and 'find' command to get file names and file type in same csv?

I would like to get filenames, creation dates, modification dates and file mime-types from directory structure. I've made a script which reads as follows :
#!/bin/bash
output="file_list.csv"
## columns
echo '"File name";"Creation date";"Modification date";"Mime type"' > $output
## content
find $1 -type f -printf '"%f";"%Tc";"%Cc";"no idea!"\n' >> $output
which gives me encouraging results :
"File name";"Creation date";"Modification date";"Mime type"
"Exercice 4 Cluster.xlsx";"ven. 27 mars 2020 10:35:46 CET";"mar. 17 mars 2020 19:14:18 CET";"no idea!"
"Exercice 5 Bayes.xlsx";"ven. 27 mars 2020 10:36:30 CET";"ven. 20 mars 2020 16:18:54 CET";"no idea!"
"Exercice 3 Régression.xlsx";"ven. 27 mars 2020 10:36:46 CET";"mer. 28 août 2019 17:21:10 CEST";"no idea!"
"Archers et Clustering.xlsx";"ven. 27 mars 2020 10:37:34 CET";"lun. 16 mars 2020 14:12:05 CET";"no idea!"
...
but I'm missing a capital thing : how do I get the files mime-types ? It would be great if I could chain the command 'mimetype -b' on each file found with 'find' command, and write it in the convenient column.
Thanks in advance,
Cyril
You might try using the -exec option of the find command, in which the brackets {} represent the name of the current file.
Then, you could remove the new line when appending to an existing file: AFAIK default behavior automatically appends new content to a new line, so the \n should not be necessary.
Last, you want to have a closing quote after your mimetype, so you should not only use the -b option, but the --output-format one, which will give you more control over what you want to display.
Hence the third command of your script should look like this:
find $1 -type f -printf '"%f";"%Tc";"%Cc";"' -exec mimetype --output-format %m\" {} \; >> $output
This is what I came up with:
for entry in *; do stat --printf='"%n";"%z";"%y";"' $entry; file -00 --mime-type $entry | cut -d $'\0' -f2; echo '"'; done
Uses a shell "for loop", to perform a stat on the directory entries in the current directory. Then uses file to get the mime type, and pipes that to cut to get only the mime type (by excluding the file name which is also printed by file).
The format for stat is what I believe was requested -- the file name, the last change date, the last modification date (both in ISO format, but could easily be made to UNIX seconds-since-epoch by upper-casing Z and Y).
Availability:
file: probably its own package if you are on Linux? But should be preinstalled on macOS I'm guessing.
bash/zsh: easily accessible both on Linux and macOS.
stat and cut: part of coreutils so should be preinstalled on most systems.

Creation of files with control of the names

I have n files, named f1, f2, ..., fn. For each of these files, I have to execute a sed command, and name the new files as file1, file2, ..., filen.
I need the new files to keep the same number as their original ones. Can anyone help?
Here's what I've tried so far:
#!/bin/sh
for element in *
do
echo "$element" sed -n '/Col3/p' $element > Quest $element
done
If we assume that all the your files are in the form in your question...
$ ls -l
total 2
-rw-r--r-- 1 ghoti wheel 0 Jan 3 13:20 f1
-rw-r--r-- 1 ghoti wheel 0 Jan 3 13:20 f2
-rw-r--r-- 1 ghoti wheel 0 Jan 3 13:20 f3
-rw-r--r-- 1 ghoti wheel 0 Jan 3 13:20 f4
then you're on the right track with a for loop. But you probably want to narrow your search to only the files that are important to you.
In bash, you can use extglob to control this sort of thing. For example:
#!/usr/bin/env bash
shopt -s extglob
for file in +([a-z])+([0-9]); do
echo "Old: $file / New: file${file##[a-z]}"
done
This matches any files whose names consist of letters followed by numbers.
If, on the other hand, you want to make this portable, so that it will work in a POSIX shell (since in your question you've specified /bin/sh), you might put the detection into the loop itself:
#!/bin/sh
for file in *; do
if ! expr "$file" : '[a-z][a-z]*[0-9][0-9]*$' >/dev/null; then
continue
fi
echo "Old: $file / New: file${file##[a-z]}"
done
In both of these examples, we use POSIX "Parameter Expansion" to strip off the letters at the beginning of the filename.
#!/bin/env bash
for FILE in *
do
[[ "$FILE" =~ [0-9]+$ ]] && mv "$FILE" file${BASH_REMATCH[0]}
done
The expression within [[ ]] is a test, which tests for a match against a regular expression, which looks for a string ending in a number. If the match is successful, the matched number can be found in the bash array variable BASH_REMATCH at index 0. The part after && is executed if the test succeeds, and renames the file to fileNN,

Hex to TCP Port - BASH

so i have been working a lot with sending hex to a tcp port. Now, my next task is to do the following , BUT this is a different way of hex?
I guess i need some help because the documentation anywhere is really bad.
So far the hex i am told (the command) is like so
01 53 20 00 41 04 4F
so normally, i would do the following in linux
exec 3<>/dev/tcp/IP OF SERVER/PORT TO SERVER
then
echo -ne '01 53 20 00 41 04 4F' >&3
then
echo <&3
but i get no reply back just blank.
Sorry i forgot to mention,
what i am use to doing is
echo -ne 54686973776f726b7366696e65 | perl -pe 's/([0-9a-f]{2})/chr hex $1/gie' >&3
and then
echo <&3
and ill get a reply.
So my question is, what is the diff between 01 53 20 etc ..
I am a bit confused.
When you say
echo -ne 54686973776f726b7366696e65 | perl -pe 's/([0-9a-f]{2})/chr hex $1/gie' >&3
you're piping your hex codes through Perl, which is breaking them up and translating them into 8-bit character codes for you.
Bash, on the other hand, doesn't handle text as well as Perl does (which is why you needed Perl in the first place). At best, because you're not doing any translation whatsoever, the other side will see the literal text 01 53 20 00 41 04 4F.
In order to do this entirely in Bash, you'd have to do something like
echo -ne '\x01\x53\x20\x00\x41\x04\x4f' >&3
The \x## codes are basically the equivalent of what Perl was doing with each pair of digits...and -e enables that translation.
For reference, this works just fine for me:
exec 3<>/dev/tcp/127.0.0.1/80
# 'GET /\r\n'
echo '\x47\x45\x54\x20\x2f\x0d\x0a' >&3
# Note: `echo <&3` didn't work here, in my tests.
cat <&3

Add blank line after every result in grep

my grep command looks like this
zgrep -B bb -A aa "pattern" *
I would lke to have output as:
file1:line1
file1:line2
file1:line3
file1:pattern
file1:line4
file1:line5
file1:line6
</blank line>
file2:line1
file2:line2
file2:line3
file2:pattern
file2:line4
file2:line5
file2:line6
The problem is that its hard to distinguish when lines corresponding to the first found result end and the lines corresponding to the second found result start.
Note that although man grep says that "--" is added between contiguous group of matches. It works only when multiple matches are found in the same file. but in my search (as above) I am searching multiple files.
also note that adding a new blank line after every bb+aa+1 line won't work because what if a file has less than bb lines before the pattern.
pipe grep output through
awk -F: '{if(f!=$1)print ""; f=$1; print $0;}'
Pipe | any output to:
sed G
Example:
ls | sed G
If you man sed you will see
G Append's a newline character followed by the contents of the hold space to the pattern space.
The problem is that its hard to distinguish when lines corresponding to the first found result end and the lines corresponding to the second found result start.
Note that although man grep says that "--" is added between contiguous group of matches. It works only when multiple matches are found in the same file. but in my search (as above) I am searching multiple files.
If you don't mind a -- in lieu of a </blank line>, add the -0 parameter to your grep/zgrep command. This should allow for the -- to appear even when searching multiple files. You can still use the -A and -B flags as desired.
You can also use the --group-separator parameter, with an empty value, so it'd just add a new-line.
some-stream | grep --group-separator=
I can't test it with the -A and -B parameters so I can't say for sure but you could try using sed G as mentioned here on Unix StackEx. You'll loose coloring though if that's important.
There is no option for this in grep and I don't think there is a way to do it with xargs or tr (I tried), but here is a for loop that will do it (for f in *; do grep -H "1" $f && echo; done):
[ 11:58 jon#hozbox.com ~/test ]$ for f in *; do grep -H "1" $f && echo; done
a:1
b:1
c:1
d:1
[ 11:58 jon#hozbox.com ~/test ]$ ll
-rw-r--r-- 1 jon people 2B Nov 25 11:58 a
-rw-r--r-- 1 jon people 2B Nov 25 11:58 b
-rw-r--r-- 1 jon people 2B Nov 25 11:58 c
-rw-r--r-- 1 jon people 2B Nov 25 11:58 d
The -H is to display file names for grep matches. Change the * to your own file glob/path expansion string if necessary.
Try with -c 2; with printing a context I see grep is separating its found o/p

Resources