Minimal, Streamlined, Powerful (Improvement for an older PR)#1419
Minimal, Streamlined, Powerful (Improvement for an older PR)#1419emrakyz wants to merge 2 commits intoLukeSmithxyz:masterfrom
Conversation
|
Used it and can confirm it works. It is a simple, short, and useful script, so I don't see any reason not to merge. Request/Addition:
As for the end-user, if it is to be used as-is without any editing, it is great to search for something in the entire pc (includes external drives!) by media type, for those moments you know a file is in your computer but have no idea where. For example, on Music, it included voice clips I have extracted from videogames, so with a simple tweak (filter by directory or by filename regex), I can split to Voice and Music. Anyway, great script. Thanks for sharing it! (and instructions to write bash) A comment on how to cron updatedb on artix systems would be a luxurious addition xaxaxa |
I prefer dcron because it's the most minimal one and I don't need advanced functionality. It should be same for the others: Automated way: Manual way: Obviously you need to start your service at the boot level. On the other hand. You are right that this script is extremely minimal, simple, extendable and documented. So the actual idea is to be enhanced by the users. As you stated, there are ways to exclude directories. Or you can even use different databases. Adding .csv is extremely easy, you can simply add it similarly to others. If you want to exclude short videos you can create a logic: If you select Videos, it can use the below command with all of the search but this would make the script slower. You need to play with this. I have never needed something like this and I think this is a very niche use case. At this point it can even be easier to use different databases instead.
|
The inspiration: (Luke Smith's Video): "Unix Chad JUST WON'T STOP Piping!"
Execution Time: Almost instant with more than a million files.
[dmenu]Select a category (Video, Music, Image, Doc, Office).[dmenu]See all of the files in the related category (withoutpathandextensions, justnames).[mpv | zathura | nsxiv | libreoffice]Open the selected file directly.REQUIREMENTS
plocatebecause it is extremely fast and light:pacman -S plocatesudo updatedb -o "${XDG_CONFIG_HOME}/.p.db" -U "/"Why Is It Good for Luke's Audience?
functions,case statements,checks,command chaining with logical control operators,pipes,variables,proper variable handling,data handling,command substitution,printing,searching,splitting preventionandsplittingwhen needed,regular expressionsandtext manipulation. More importantly, I aim for it to be thought-provoking in contrast with these technical aspects.Explanations for Everything for Learners
Create a function named
f. Functionsf() { ... }are for creating command blocks to be used later. Since we will need to use this function multiple times; it is better to streamline rather than to write the same commands again and again. This improves minimalism and streamlinedness while enhancing portability and modularity at the same time.r="$(locate -d "${XDG_CONFIG_HOME}/.p.db" -b -r ".*\.\(${1}\)$")"variablenamedr.command substitutionwith$()and put it inside double quotes to prevent splitting (After the command runs and it generates an output, some characters such as spaces can induce splitting which makes a single variable seem like two different variables. Sometimes this is needed but most of the time it's not).Command substitutionmeans: Run the command and generate an output. Since it's inside a variable; the generated output becomes ourvariable.locatecommand with the pre-generated database named.p.dbwhich is located inside the user configuration directory.config.-dflag is used before we show the location of the database.-bflag is used for basename matching, excluding directory matches from the list.-rmeans regular expressions. After this flag we show the command the exact search phrase using regex. In this case we aim to match for:.*Means any character.in any amount*. According to regex, dot matches everything 0 or more times.\.a literal dot. Backslash is used; otherwise it is interpreted as any character rather than a dot because again, dot means any character in regex. Two of these matches aim to match everything up until and including the last dot of found file names.\( \)which we also escape with backslashes is for capturing the extensions in a group. Parentheses are used for grouping and escaped to ensure they are not interpreted as normal characters. Inside those parentheses, we will have our extension lists.$1in this case means the first argument for our function which will take up extensions. In this example:f argumentthe word argument becomes$1. In this case we use curly brackets around the variable to ensure differentiating. Curly brackets${1}differentiate the variable name from the surrounding text. The variable was already inside quotes so we didn't have to put quotes exclusively. In fact putting extra exclusive quotes, would actually unquote the said variable. It is the best practice to always use quotes and curly brackets with variables on shells to prevent splitting and ensure differentiating. In some rare cases (which we also have in this script) we may want to split the output.o="$(printf "%s\n" "${r}" | sed 's|.*/||;s/\.[^.]*$//' | dmenu -i -p "${c}" -l "20")"o.$(), again to be quoted.|. Pipes take the standard output of the prior command and send it to the next command as an input.printfis for printing to standard output (i.e your terminal)."%s\n"means print all variables%sand add new lines\nafter each of them.printfgenerally accepts two arguments inside quotes. In this case, one is "%s\n" and the other one is the actual variable we want to print which is$r. Again, we put curly brackets and quotes:"${r}". "${r}" here means the output of our file search above which we namedr. So we aim to print the path of all files found which match our search pattern.printfis edited with the stream editorsed.sedgenerally accepts a replacement logic inside single quotes (double quotes are required for variable expansion). Inside we used a semicolonsed ' ; '. Semicolon or new lines or-eflag is used when you want to apply more than one replacement. On the left and the right side of the semicolons we have our actual replacement logic:smeans replace.|is used as a delimiter..in any amount*that ends with a forward slash/. This is the complete representation:.*/.|as a delimited instead of a forward slash because we already have a forward slash character for our replacement so we needed to make it less complex and easier to read. That's whysedaccepts different delimiters..*/with nothing by using one more delimiter immediately|.For the second replacement in
sed; we match a literal dot by escaping the dot with a backslash\.and then we match anything but a dot[^.]. Normally^means the start of the line but inside brackets^means not. So, it means: Match anything but a dot.We inform
sedthat it can repeat the last matched character which is anything but a dot can repeat 0 or more times[^.]*. So it's completely optional but if the searched character is there, it will definitely match that no matter how many times it happens (we mainly want to target the extension here such asmp3.We also inform
sedthat the last match would be our end of the line$. Dollar sign means the end of the line in regular expressions. So, to sum up we matched a dot and anything but a dot at the end of the line in any amount\.[^.]*As an example this could be.mp3or.xlsxThen we also replaced the second match with nothing effectively deletingthe extension.
At this point we have raw filenames divided from their paths and extension. For instance, instead of
/path/to/the/video_file.mp4we havevideo_fileso we can see, navigate, filter in a better way.We pipe the output of the text manipulated lines into
dmenuto interactively see, and filter them.-iflag for dmenu allows us to use uppercase letters in our filters.-pflag is for naming the title of the menu. We have the title"${c}"based on our category selection which will be explained later.-lis for using a vertical menu with the indicated number of lines20.[ "${o}" ] && ${2} "$(printf "%s\n" "${r}" | grep -Fm "1" "/${o}.")"We check if the variable
ois empty or not. Normally this is checked by the-nflag such as[ -n $VAR ]but the other possibility is to just write the variable inside brackets.After the check, we have the control operator
&&. This means, apply the next command on this line, only if the prior command succeeds. So if[ "${o}" ]returns a fail meaning if the variableois empty, the shell will try to jump to the next line. If it exists, it will continue with${2}. Since we are still in theffunction, $2 shows the second argument added to the function such asf first second. In this time we still use double brackets but we don't use quotes because ${2} will target shell commands. We want this output to be interpreted as different arguments. For examplempv --no-audioconsists of two different arguments so if we use double quotes; this will be interpreted as a single argument which would lead this command to be failed.So let's say
"${o}"is not empty and${2}ismpv. We create another command substitution$()whose output will be used bympvsuch asmpv video_file.mkv.We print the search output again with
printf(the raw output which the path and the extension not removed). Then we pipe the output, this time intogrepto search for patterns inside the text input. We assumed"${o}"is not empty which corresponds to a selected file name. So we print the raw output"${r}"and search a forward slash and the selected filename and the dot/filename.but selected filename is"${o}"in our script so it is shown as"/${o}.". You may wonder why we do not escape the last dot.grepwith its-Fflag disables regular expression matching, operating faster and enables literal match. We already have the complete file name so we don't need a regex match. The forward slash/shows the last separator to the file path and then${o}shows the filename and then.shows the last character before the extension.-m 1is for printing only one file (if there are multiple files with the exact same name) because we can't open two files at the same time.The above part was the actual logic of the script. The second part is higher level.
c="$(printf "Video\nDoc\nImage\nOffice\nMusic\n" | dmenu -i -p "Categories" -l "5")"Create a variable named
c; and abbreviation for categories.Print the categories we have (similar categories can be added as
\nNewCategory).Send the available categories to the
dmenuwith theCategoriestitle.After the selection "${c}" will become on of these categories.
Then we have a case statement that is based on the variable
c.case "${c}" in "Video") command ;;"Video", run the command on this line up until;;"Video") f "mp4\|mkv\|webm\|mov\|m4v\|wmv\|flv\|avi\|gif\|m2ts" "mpv" ;;Videocategory is selected, use theffunction which is the only function we have on this script; with these extensions as its first argument"${1}"and"mpv"as its second argument affectively showingmpvas the opener and video extensions for the search pattern. We used\|between extensions because it's required for the regex syntax forlocate. We escape the OR|delimiter with a backslash to make each of them literal.All other cases use the similar logic; and then we close the case statement with
esac.