Change Powerlevel9k segment based on current folder - customization

I'm trying to customize my zsh terminal, got most things the way I wanted, but then I ran into a page showing how to make language segments to show Javascript, Python, etc. I've tried the following code on my .zshrc file:
lang_segment () {
dir="$(dirname "$PWD")"
if [[ $dir == *"Dev/python"* ]] ; then
echo -n "%{%F{yellow}%K{blue}'\uf81f' Python}"
fi
if [[ $dir == *"Dev/javascript"* ]] ; then
echo -n "%{%F{black}%K{yellow}'\ue781' Javascript}"
fi
}
POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(os_icon user dir custom_lang_segment vcs)
POWERLEVEL9K_CUSTOM_LANG_SEGMENT="lang_segment"
I'm using powerlevel9k theme. My idea is that when you cd into a folder inside the dev/python folder, a segment is shown with Python text, icon and color scheme, same for the dev/javascript folder or any language folder I use for that matter. As it is, nothing shows up. So my question is, is it possible to do it?

I managed to solve the issue after some tinkering, the code looks like this:
prompt_lang_segment () {
# Looks up the folder path + name
dir="$( cd "$(dirname "$0")" ; pwd -P )"
# Checks if it is the desired language/framework folder, in this case, python
if [[ $dir == *"Dev/python"* ]] ; then
content="\uf81f Python%f"
"$1_prompt_segment" "$0" "$2" "blue" "yellow" "$content" "#"
fi
if [[ $dir == *"Dev/js"* ]] ; then
content="\ue781 Javascript%f"
"$1_prompt_segment" "$0" "$2" "yellow" "black" "$content" "#"
fi
...
}
# Added the lang_segment to the left prompt
POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(os_icon user dir lang_segment vcs)
I had to make a custom segment using prompt (prompt_lang_segment instead of just lang_segment) on the function and to work with it I had to change this:
echo -n "%{%F{black}%K{yellow}'\ue781' Javascript}"
into the default powerlevel9k command to show segments:
"$1_prompt_segment" "$0" "$2" "yellow" "black" "$content" "#"
So I added an if statement for each language/framework I had a folder for at this time and I also had to change the folder name javascript to js because it was showing both javascript and java segments.
This configuration works for the language/framework folder and all subfolders of it.
This is the end result:

Related

Regex to replace variables in configuration file

I have got three types of variables in my config file which are used by bash scripts in different functions
PACKAGES=(base linux sudo neovim)
HOOKS=()
VOLUMES=()
UEFI="yes"
BOOTLOADER="efistub"
PATH="/dev/mapper/luks"
I have one function which is updating them by interacting with user or directly from config file
set_option() {# with two args
if [[ "$1" =~ "PACKAGES" ]]; then
sed -i "/PACKAGES=/s/\)/\ $2\)/" "$CONFIG_FILE"
elif grep -Eq "^$1.*" "$CONFIG_FILE"; then
sed -i -E "/$1=/s/REGEX/\"$2\"/" "$CONFIG_FILE"
else
sed -i -E "s/$1=REGEX/$1=$2/" "$CONFIG_FILE"
fi
}
set_option is working fine for PACKAGES it is adding new packages to the list like
set_option "PACKAGES" "git"
I am struggling with
set_option to update variable like HOOKS or VOLUMES and replace anything inside or empty () with new values i.e. (lvm2 filesystems) or (# #home #tmp) respectively.
set_option to update variable like UEFI or PATH which have quoted strings with new values in quotations i.e. no or /dev/mapper/crypt

How to rename files using certain string inside each file?

I have a list of html files that contain a certain tag in each file as below:
<div id="myID" style="display:none">1_34876</div>
I would like to search for that tag in each file and rename each file according to the number within that tag, i.e rename the file containing the tag above to 1_34876.html
and so forth..
Is there a regex or bash command using grep or awk that can accomplish this?
So far I was able to grep each file using the following command but stuck on how to rename the files:
grep '<div id="myID" style="display:none">.*</div>' ./*.html
An additional bonus would be if the command doesn't overwrite duplicate files, e.g. if another file contains the 1_34876 tag above then the second file would be renamed as 1_34876 v2.html or something similar.
Kindly advice if this can be achieved in a way that doesn't require programming.
Many thanks indeed.
Ali
You can use the following script to achieve your goal. Note, for the script to work on macOS, you either have to install GNU grep via Homebrew, or substitute the grep call with ggrep.
The script will search the current directory and all its subdirectories for *.html files.
It will substitute only the names of the files that contain the specific tag.
For multiple files that containt the same tag, each subsicuent file apart from the first will have an identifier appended to its name. E.g., 1_234.html, 1_234_1.html, 1_234_2.html
For files that contain multiple tags, the first tag encountered will be used.
#!/bin/bash
rename_file ()
{
# Check that file name received is an existing regular file
file_name="$(realpath "${1}")"
if [ ! -f "${file_name}" ]; then
echo "No argument or non existing file or non regular file provided"
exit 1
fi
# Get the tag number. If the number does not exist, the variable tag will be
# empty. The first tag on a file will be used if there are multiple tags
# within a file.
tag="$(grep -oP -m 1 '(?<=<div id="myID" style="display:none">).*?(?=</div>)' \
-- "${file_name}")"
# Rename the file only if it contained a tag
if [ -n "${tag}" ]; then
file_path="$(dirname "${file_name}")"
# Change directory to the file's location silently
pushd "${file_path}" > /dev/null || return
# Check for multiple occurences of files with the same tag
if [ -e "${tag}.html" ]; then
counter="$(find ./ -maxdepth 1 -type f -name "${tag}.html" -o -name "${tag}_*.html" | wc -l)"
tag="${tag}_${counter}"
fi
# Rename the file
mv "${file_name}" "${tag}.html"
# Return to previous directory silently
popd > /dev/null || return
fi
}
# Necessary in order to call rename_file from find command within main
export -f rename_file
# The entry point function of the script. This function searches for all the
# html files in the directory that the script is run, and all subdirectories.
# The function calls rename_files upon each of the found files.
main ()
{
find ./ -type f -name "*.html" -exec bash -c 'rename_file "${1}"' _ {} \;
}
main
As we do not know exact content of the files and folders we need to take care about
Check that tag is unique
File with such name does not exists
Do not rename already renamed files
Steps:
Search for files with specidied tags grep -o '<div id="myID" style="display:none">.*</div>' /*.html
it will provide us output in format file_path:tag
Extract file_path using regexp [^:]+ (not : one or more symbols )
with -o (exact match) and get first occurence using head -1
Get new file name from tag using regexp ">.<" and then remowing "<>" using sed
we can use here one regexp to skip <> but it will be more complex and not so readable grep -oP ">\K.(?=<)"
Rename files , log Errors and script actions, return non zero exit code in case of issues
Unless it is really required better to use functions with local variables declaration (decalre using function fn_rename_html_files {} instead of fn_rename_html_files() {} as if we have complex logic with nesting functions you may change other functions variables and debugging will be not so easy)
function fn_rename_html_files {
typeset l_files_path="${1-.}"
typeset l_html_ext=".html"
typeset l_proc_exit_code=0
typeset l_proc_name="${FUNCNAME[0]}"
typeset l_files_to_search="${l_files_path}/*${l_html_ext}"
typeset l_tag_reg_exp='<div id="myID" style="display:none" *>.*</div>'
typeset l_matched_files=$(grep -o "$l_tag_reg_exp" $l_files_to_search | sort -u)
typeset l_prev_file_path=""
# loop through matching files
while IFS= read -r l_file_path_with_match_tag
do
l_current_file_path=$(echo "$l_file_path_with_match_tag" | egrep -o "([^:])+" | head -1 )
l_current_file_name=$(basename "$l_current_file_path")
#echo "l_current_file_path $l_current_file_path "
#echo "l_current_file_name $l_current_file_name "
l_new_file_name_from_tag=$(echo "$l_file_path_with_match_tag" | egrep -o ">.*<" | sed "s/[><]//g" | head -1 )
l_new_file_name="${l_new_file_name_from_tag}${l_html_ext}"
l_new_file_path="$l_files_path/$l_new_file_name"
#echo "$l_new_file_path $l_new_file_path "
#echo "$l_new_file_name $l_new_file_name "
if [[ "$l_prev_file_path" == "$l_current_file_path" ]]; then
echo "$l_proc_name ERROR: myID tag is not unique for $l_current_file_path, skipping second renaming " >&2
let l_proc_exit_code+=1
continue
fi
if ! [[ -f "$l_new_file_path" ]]; then
if mv "$l_current_file_path" "$l_new_file_path" ; then
echo "$l_proc_name SUCCESS: renamed $l_current_file_path to $l_new_file_path"
else
echo "$l_proc_name ERROR: cannot move $l_current_file_path to $l_new_file_path" >&2
let l_proc_exit_code+=1
fi
else
if [[ "$l_current_file_name" == "$l_new_file_name" ]]; then
echo "$l_proc_name Warning: File has been already renamed: $l_current_file_name, skipping"
else
echo "$l_proc_name Warning: File with such name already exists: $l_current_file_name, skipping" >&2
let l_proc_exit_code+=1
fi
fi
l_prev_file_path=$l_current_file_path
done <<< $l_matched_files
return $l_proc_exit_code
}
# create test files
rm *.html
echo '<div id="myID" style="display:none">1_1</div>' > 1.html ;
echo '<div id="myID" style="display:none">1_2</div>' > 2.html ;
echo '<div id="myID" style="display:none">1_3</div>' > 3.html ;
echo '<div id="myID" style="display:none">1_3_1</div>' >> 3.html ;
# run
fn_rename_html_files

can't extract a substring with regex

I'm trying to write a prepare-commit-msg hook for git. The script should do following steps :
Get the current git branch name (working)
Extract the issue-id (not working)
Check if the issue-id is already in the commit msg
If not, insert [issue-id] before the commit message
The issue-id has this pattern [a-zA-Z]+-\d+ and the branch name should be something like feature/issue-id-my-small-description.
But for now, the extraction part is not ok...
Here is my prepare-commit-msg script :
# Regex used to extract the issue id
REGEX_ISSUE_ID="s/([a-zA-Z]+-\d+)//"
# Find current branch name
BRANCH_NAME=$(git symbolic-ref --short HEAD)
# Extract issue id from branch name
ISSUE_ID= $BRANCH_NAME | sed -r $REGEX_ISSUE_ID
# Check if the issue id is already in the msg
ISSUE_IN_COMMIT=$(grep -c "\[$ISSUE_ID\]" $1)
# Check if branch name is not null and if the issue id is already in the commit msg
if [ -n "$BRANCH_NAME" ] && ! [[ $ISSUE_IN_COMMIT -ge 1 ]]; then
# Prefix with the issue id surrounded with brackets
sed -i.bak -e "1s/^/[$ISSUE_ID] /" $1
fi
Edit to add in-/output example
Input $1 is the git commit message which is something like
fix bug on login
or
fix MyIssue-234 which is a bug on login
Output should be the input with the issue id i.e. :
[MyIssue-123] fix bug on login
I'm not sure about what and why you do as you do, but this is the closest I got by fixing what I thought is to be corrected in your code:
# Regex used to extract the issue id
REGEX_ISSUE_ID="s/\[([a-zA-Z]+-[0-9]+)\].*/\1/"
# Find current branch name
BRANCH_NAME=$(git symbolic-ref --short HEAD)
if [[ -z "$BRANCH_NAME" ]]; then
echo "No brach name... "; exit 1
fi
# Extract issue id from branch name
ISSUE_ID=$(echo "$BRANCH_NAME" | sed -r "$REGEX_ISSUE_ID")
# Check if the issue id is already in the msg
ISSUE_IN_COMMIT=$(echo "$#" | grep -c "^\[*$ISSUE_ID\]*")
# Check if branch name is not null and if the issue id is already in the commit msg
if [[ -n "$BRANCH_NAME" ]]; then
if [[ $ISSUE_IN_COMMIT -gt 0 ]]; then
shift # Drop the issue if from the msg
fi
# Prefix with the issue id surrounded with brackets
MESSAGE="[$ISSUE_ID] $#"
fi
echo "$MESSAGE"
where $# is all the words that you provide after "fix" (ex. "$#" = "bug" "on" "login"). The rest I hope you understand after you compare it to your original code.

how to substitute the variable in shell script

In the below script,I want to add the file name as prefix to each output line generated by grep.
I dont know why this is not replacing the filename with $file,I am getting the $file as the prefix.can anyone help me on this
function traverse() {
for file in "$1"/*
do
if [ ! -d "${file}" ] ; then
if [ ${file: -2} == ".c" ]
then
./sed.sed "$file" > latest_log.txt #To remove all the comments
grep -nir "$2" latest_log.txt >> output.log #grep matched lines
sed -i "s/^/$file/" output.log > grepoutput3.txt #prefix filename($file here)
echo "${file} is a c file"
fi
else
traverse "${file}" "$2"
fi
done
}
function main() {
traverse "$1" "$2"
}
main "$1" "$2"
The below line should add the filename as prefix,but $file not replacing,Apart from that whole script is working fine.
sed -i "s/^/$file/" ex.txt > grepoutput3.txt
EX: search for "welcome" in all .c files of a folder.Take first_file.c is one.
FIRST_FILE.C
welcome here
/* welcome here */
//welcome here
welcome here2
Expected output
/DIR/FIRST_FILE.C:1: welcome here
/DIR/FIRST_FILE.C:4: welcome here2
with my script ,output is
1: welcome here
4: welcome here2
So,I am trying to prefix the file name (/DIR/FIRST_FILE.C) to each line.we can fetch that filename from $file,But sed is not interpreting it .
Nowhere in your script is any code that creates ex.txt. Is that intended?
To debug this, run your script under set -x near the beginning.
Also worth noting: you have tagged this question "sh" (not bash) but
if [ ${file: -2} == ".c" ]
is full of nonportable bashisms: the string equality operator is = and the ${file: -2} syntax is not understood by a POSIX sh. An equivalent sh code would use
case $file in
(*.c) ...;;
esac
For the filesystem traversal: Use find!
find FOLDER -type f -name '*.c' -exec bash yourscript.sh {} \;
In yourscript.sh you can access the filename in $1.
I could help further with implementing that script but it is pretty unclear what you are trying to do.

Regex doesn't work using loop to go through filenames

I've been working on this for a while now and I can't seem to crack it. I have in a folder some files
testing.test.S09E01.720p.HDTV.x264-TWIST
dmscript.sh
I have the script in there to test it before I set it up with download manager to run once an episode has been downloaded.
The script has the following code.
#!/bin/sh
#THIS SCRIPT WILL DETERMINE TV SHOWS AND EPISODES AND MOVE THEM TO THE CORRECT
#FOLDER WHICH WILL ALLOW EITHER SICKBEARD OR COUCHPOTATO TO RENAME AND MOVE THEM
regex="([Ss]?([0-9]{1,2})[-|.]?[x|Ee|]?(\d{2})|(0?\d{1})(\d{2}))"
#where the tv shows will be copied to
tvdir="/volume1/public/NZB/Complete/tv/processed/"
#this was a debug variable to allow me to see where the code fails
num="1"
#will change the * to full path of folder later, for now this works because script is running from
#inside folder
for filename in *
do
if [[ "$filename =~ $regex ]]
then
#display the current filename and the variable number
echo "$filename $num"
#commented on the following code so when the script works I will just uncomment
#mv $filename $tvdir
#change variable to 2, this is to see whether the if test will fail and skip the file
#that doesn't conform to the regex
num="2"
else
echo "nothing of use"
done
However, once I run the code, I get this
testing.test.S09E01.720p.HDTV.x264-TWIST 1
dmscript.sh 2
obviously something goes wrong, as I just want it to display the first in the list above and ignore the other.
I got the regex rule from http://regex101.com/r/qZ2eO9/1 , I ignored the /gim at the end as I am unsure whether this will work in shell, and just stuck with the Ss and Ee so it isn't case sensitive
A few modifications:
(1) fix the \d into [0-9] in the expression.
(2) fix the quotation mark " in the line if [[ "$filename =~ $regex ]]
(3) add fi in the end of the if block
And now the script should work.
regex="([Ss]?([0-9]{1,2})[-|.]?[x|Ee|]?([0-9]{2})|(0?[0-9]{1})([0-9]{2}))"
#where the tv shows will be copied to
tvdir="/volume1/public/NZB/Complete/tv/processed/"
#this was a debug variable to allow me to see where the code fails
num="1"
#will change the * to full path of folder later, for now this works because script is running from
#inside folder
for filename in *
do
if [[ $filename =~ $regex ]]
then
#display the current filename and the variable number
echo "$filename $num"
#commented on the following code so when the script works I will just uncomment
#mv $filename $tvdir
#change variable to 2, this is to see whether the if test will fail and skip the file
#that doesn't conform to the regex
num="2"
else
echo "nothing of use"
fi
done