Browse Docs

Scripting

Scripting sections in docs

Documentation regarding scripting (usually in devops or sysadmin context).

In this section

  • ๐Ÿ‘ฎ Justfile

    Interesting example from justfile documentation:
    where it create mktemp and set it in variable then by concatenation you get a full path to the tar.gz.
    Then the Recipe “publish” create the artifact again and push it to a server.

     1tmpdir  := `mktemp`  # Create a tmp file
     2version := "0.2.7"   
     3tardir  := tmpdir / "awesomesauce-" + version
     4tarball := tardir + ".tar.gz"  # use tmpfile path to create a tarball
     5
     6publish:
     7  rm -f {{tarball}}
     8  mkdir {{tardir}}
     9  cp README.md *.c {{tardir}}
    10  tar zcvf {{tarball}} {{tardir}}
    11  scp {{tarball}} me@server.com:release/
    12  rm -rf {{tarball}} {{tardir}}
    

    This one can be really usefull to define a default value which can be redefine with env variable:

  • ๐Ÿ‘ท Makefile

    Shell Variable

    $$var $$( python -c ‘import sys; print(sys.implementation.name)’ )

    Make Variable

    T ?= foo # give a default value T := $(shell whoami) # execute shell immediately to put in the var

    PHONY to execute several makefile

    Example 1

    1SUBDIRS = foo bar baz
    2
    3## dir is a Shell variables
    4## SUBDIR and MAKE are Internal make variables
    5subdirs:
    6        for dir in $(SUBDIRS); do \
    7          $(MAKE) -C $$dir; \
    8        done
    

    Example 2

    1SUBDIRS = foo bar baz
    2
    3.PHONY: subdirs $(SUBDIRS)
    4subdirs: $(SUBDIRS)
    5$(SUBDIRS):
    6        $(MAKE) -C $@
    7foo: baz
    

    Idea for a testing tools

    1git clone xxx /tmp/xxx&& make -C !$/Makefile
    2make download le conteneur
    3make build le binaire
    4make met le dans /use/local/bin
    5make clean
    6make help
    

    Sources:

    Tutorials

  • Golang
    • ๐Ÿ Cobra

      A Command Builder for Go

      Usefull

      • Installation
      1# Install GO
      2GO_VERSION="1.21.0"
      3wget https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz
      4sudo tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz
      5export PATH=$PATH:/usr/local/go/bin
      6
      7# Install Cobra - CLI builder
      8go install github.com/spf13/cobra-cli@latest
      9sudo cp -pr ./go /usr/local/.
      
      • Init
      1mkdir -p ${project} && cd ${project}
      2go mod init ${project}
      3cobra-cli init
      4go build
      5go install
      6cobra-cli add timezone
      
    • ๐Ÿ‘ฎ CUE-lang

      CUE stands for Configure, Unify, Execute

      Basics

      • Installation
       1# Install GO
       2GO_VERSION="1.21.0"
       3wget https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz
       4sudo tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz
       5export PATH=$PATH:/usr/local/go/bin
       6
       7go install cuelang.org/go/cmd/cue@latest
       8sudo cp -pr ./go /usr/local/.
       9
      10# or use Container
      11printf "\e[1;34m[INFO]\e[m Install CUElang:\n";    
      12podman pull docker.io/cuelang/cue:latest
      
      • concepts

      top -> schema -> constraint -> data -> bottom

      • Command
       1# import a file 
       2cue import imageset-config.yaml 
       3
       4# Validate 
       5cue vet imageset-config.cue imageset-config.yaml
       6
       7
       8* Some basics example
       9
      10```go
      11// This is a comment
      12_greeting: "Welcome" // Hidden fields start with "_"
      13#project:  "CUE"     // Definitions start with "#"
      14
      15message: "\(_greeting) to \(#project)!" // Regular fields are exported
      16
      17#Person: {
      18  age: number            // Mandatory condition and must be a number
      19  hobbies?: [...string]  // non mandatory but if present must be a list of string
      20}
      21
      22// Constrain which call #Person and check if age
      23#Adult: #Person & {
      24  age: >=18
      25}
      26
      27// =~ match a regular expression
      28#Phone: string & =~ "[0-9]+"
      29
      30// Mapping
      31instanceType: {
      32    web: "small"
      33    app: "medium"
      34    db:  "large"
      35}
      36
      37server1: {
      38    role:     "app"
      39    instance: instanceType[role]
      40}
      41
      42// server1.instance: "medium"
      
      • Scripting
      1# executable have extension name "_tool.cue"
      2
      3# usage
      4cue cmd prompter
      
       1package foo
       2
       3import (
       4	"tool/cli"
       5	"tool/exec"
       6	"tool/file"
       7)
       8
       9// moved to the data.cue file to show how we can reference "pure" Cue files
      10city: "Amsterdam"
      11
      12// A command named "prompter"
      13command: prompter: {
      14
      15	// save transcript to this file
      16	var: {
      17		file: *"out.txt" | string @tag(file)
      18	} // you can use "-t flag=filename.txt" to change the output file, see "cue help injection" for more details
      19
      20	// prompt the user for some input
      21	ask: cli.Ask & {
      22		prompt:   "What is your name?"
      23		response: string
      24	}
      25
      26	// run an external command, starts after ask
      27	echo: exec.Run & {
      28		// note the reference to ask and city here
      29		cmd: ["echo", "Hello", ask.response + "!", "Have you been to", city + "?"]
      30		stdout: string // capture stdout, don't print to the terminal
      31	}
      32
      33	// append to a file, starts after echo
      34	append: file.Append & {
      35		filename: var.file
      36		contents: echo.stdout // because we reference the echo task
      37	}
      38
      39	// also starts after echo, and concurrently with append
      40	print: cli.Print & {
      41		text: echo.stdout // write the output to the terminal since we captured it previously
      42	}
      43}
      

      Sources

      Offical Documentation

  • PowerShell
    • Mysql

      Example

       1# Import values with details connexion
       2. .\values.ps1
       3
       4$scriptFilePath ="$MyPath\Install\MysqlBase\Script.sql"
       5
       6# Load the required DLL file (depend on your connector)
       7[void][System.Reflection.Assembly]::LoadFrom("C:\Program Files (x86)\MySQL\MySQL Connector Net 8.0.23\Assemblies\v4.5.2\MySql.Data.dll")
       8
       9# Load in var the SQL script file
      10$scriptContent = Get-Content -Path $scriptFilePath -Raw
      11
      12# Execute the modified SQL script
      13$Connection = [MySql.Data.MySqlClient.MySqlConnection]@{
      14    ConnectionString = "server=$MysqlIP;uid=$MysqlUser;Port=3306;user id=$MysqlUser;pwd=$MysqlPassword;database=$MysqlDatabase;pooling=false;CharSet=utf8;SslMode=none"
      15    }
      16    $sql = New-Object MySql.Data.MySqlClient.MySqlCommand
      17    $sql.Connection = $Connection
      18    $sql.CommandText = $scriptContent
      19    write-host $sql.CommandText
      20    $Connection.Open()
      21    $sql.ExecuteNonQuery()
      22    $Connection.Close()
      
    • Parsing

      POO

      1# Convert your json in object and put it in variable
      2$a = Get-Content 'D:\temp\mytest.json' -raw | ConvertFrom-Json
      3$a.update | % {if($_.name -eq 'test1'){$_.version=3.0}}
      4
      5$a | ConvertTo-Json -depth 32| set-content 'D:\temp\mytestBis.json'
      

      Example updating a XML

       1#The file we want to change
       2$xmlFilePath = "$MyPath\EXAMPLE\some.config"
       3
       4   # Read the XML file content
       5   $xml = [xml](Get-Content $xmlFilePath)
       6
       7   $node = $xml.connectionStrings.add | where {$_.name -eq 'MetaData' -And $_.providerName -eq 'MySql.Data.MySqlClient'}
       8   $node.connectionString = $AuditDB_Value
       9
      10   $node1 = $xml.connectionStrings.add | where {$_.name -eq 'Account'}
      11   $node1.connectionString = $Account_Value
      12
      13   # Save the updated XML back to the file
      14   $xml.Save($xmlFilePath)
      15
      16   Write-Host "$xmlFilePath Updated"
      

      Nested loop between a JSON and CSV

       1# Read the JSON file and convert to a PowerShell object
       2$jsonContent = Get-Content -Raw -Path ".\example.json" | ConvertFrom-Json
       3
       4# Read CSV and set a Header to determine the column
       5$csvState = Import-CSV -Path .\referentials\states.csv -Header "ID", "VALUE"  -Delimiter "`t"
       6# Convert in object
       7$csvState | ForEach-Object { $TableState[$_.ID] = $_.VALUE  }
       8
       9# Loop through the Entities array and look for the state
      10foreach ($item in $jsonContent.Entities) {
      11    $stateValue = $item.State
      12
      13    # Compare the ID and stateValue then get the Value
      14    $status = ($csvState | Where-Object { $_.'ID' -eq $stateValue }).VALUE
      15
      16    Write-Host "Status: $status"
      17}
      

      Sources

      https://devblogs.microsoft.com/powershell-community/update-xml-files-using-powershell/

  • Python
    • ๐ŸŒ… UV

      Install

      1# curl method
      2curl -LsSf https://astral.sh/uv/install.sh | sh
      3
      4# Pip method
      5pip install uv
      

      Quick example

      1pyenv install 3.12
      2pyenv local 3.12
      3python -m venv .venv
      4source .venv/bin/activate
      5pip install pandas
      6python
      7
      8# equivalent in uv
      9uv run --python 3.12 --with pandas python
      

      Usefull

      1uv python list --only-installed
      2uv python install 3.12
      3uv venv /path/to/environment --python 3.12
      4uv pip install django
      5uv pip compile requirements.in -o requirements.txt
      6
      7uv init myproject
      8uv sync
      9uv run manage.py runserver
      

      Run as script

      • Put before the import statements:
      1#!/usr/bin/env -S uv run --script
      2# /// script
      3# requires-python = ">=3.12"
      4# dependencies = [
      5# "ffmpeg-normalize",
      6# ]
      7# ///
      

      Then can be run with uv run sync-flickr-dates.py. uv will create a Python 3.12 venv for us. For me this is in ~/.cache/uv (which you can find via uv cache dir).

    • ๐Ÿ‘พ Pypi Repository

      Pypi Repo for airgap env

      Let’s take as an example py dependencies for Netbox

       1# Tools needed
       2dnf install -y python3.11
       3pip install --upgrade pip setuptool python-pypi-mirror twine
       4
       5# init mirror
       6python3.11 -m venv mirror
       7mkdir download
       8
       9# Get list of Py packages needed
      10curl raw.githubusercontent.com/netbox-community/netbox/v3.7.3/requirements.txt -o requirements.txt
      11echo pip >> requirements.txt
      12echo setuptools >> requirements.txt
      13echo uwsgi >> requirements.txt
      14
      15# Make sure repository CA is installed
      16curl http://pki.server/pki/cacerts/ISSUING_CA.pem -o /etc/pki/ca-trust/source/anchors/issuing.crt
      17curl http://pki.server/pki/cacerts/ROOT_CA.pem -o /etc/pki/ca-trust/source/anchors/root.crt
      18update-ca-trust
      19
      20
      21source mirror/bin/activate
      22pypi-mirror download -b -d download -r requirements.tx
      23twine upload  --repository-url https://nexus3.server/repository/internal-pypi/ download/*.whl --cert /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
      24twine upload  --repository-url https://nexus3.server/repository/internal-pypi/ /download/*.tar.gz --cert /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
      

      Then on target host inside /etc/pip.conf :

    • ๐Ÿ”— Dependencies

      Package with pip3

      1pip3 freeze netaddr > requirements.txt
      2pip3 download -r requirements.txt -d wheel
      3mv requirements.txt wheel
      4tar -zcf wheelhouse.tar.gz wheel
      5tar -zxf wheelhouse.tar.gz
      6pip3 install -r wheel/requirements.txt --no-index --find-links wheel
      

      Package with Poetry

       1curl -sSL https://install.python-poetry.org | python3 -
       2poetry new rp-poetry
       3poetry add ansible
       4poetry add poetry
       5poetry add netaddr
       6poetry add kubernetes
       7poetry add jsonpatch
       8poetry add `cat ~/.ansible/collections/ansible_collections/kubernetes/core/requirements.txt`   
       9
      10poetry build
      11
      12pip3 install dist/rp_poetry-0.1.0-py3-none-any.whl
      13
      14poetry export --without-hashes -f requirements.txt -o requirements.txt
      

      Push dans Nexus

      1poetry config repositories.test http://localhost
      2poetry publish -r test
      

      Images Builder

      1podman login registry.redhat.io
      2podman pull registry.redhat.io/ansible-automation-platform-22/ansible-python-base-rhel8:1.0.0-230
      3
      4pyenv local 3.9.13
      5python -m pip install poetry
      6poetry init
      7poetry add ansible-builder 
      
  • Shell
    • ๐Ÿฆ Awk

      The Basics

      awk is treat each line as a table, by default space are separators of columns.

      General syntax is awk 'search {action}' file_to_parse.

      1# Give the value higher than 75000 in column $4
      2df | awk '$4 > 75000'   
      3
      4# Print the all line when column $4 is higher than 75000
      5df | awk '$4 > 75000 {print $0}' 
      

      But if you look for a string, the search need to be included in /search/ or ;search;.
      When you print $0 represent the all line, $1 first column, $2 second column etc.

    • ๐Ÿด Sed

      The Basics

       1sed -e 'โ€ฆ' -e 'โ€ฆ'  # Several execution 
       2sed -i             # Replace in place 
       3sed -r             # Play with REGEX
       4
       5# The most usefull
       6sed -e '/^[ ]*#/d' -e '/^$/d' <fich.>    #   openfile without empty or commented lines
       7sed 's/ -/\n -/g'                        #   replace all "-" with new lines
       8sed 's/my_match.*/ /g'                   #   remove from the match till end of line
       9sed -i '4048d;3375d' ~/.ssh/known_hosts  #   delete lines Number
      10
      11# Buffer 
      12s/.*@(.*)/$1/;                                        #  keep what is after @ put it in buffer ( ) and reuse it with $1.
      13sed -e '/^;/! s/.*-reserv.*/; Reserved: &/' file.txt  #  resuse search with &
      14
      15# Search a line
      16sed -e '/192.168.130/ s/^/#/g' -i /etc/hosts          # Comment a line 
      17sed -re 's/^;(r|R)eserved:/; Reserved:/g' file.txt    # Search several string
      18
      19# Insert - add two lines below a match pattern
      20sed -i '/.*\"description\".*/s/$/ \n  \"after\" : \"network.target\"\,\n  \"requires\" : \"network.target\"\,/g'  my_File  
      21
      22# Append
      23sed '/WORD/ a Add this line after every line with WORD'
      24
      25# if no occurence, then add it after "use_authtok" 
      26sed -e '/remember=10/!s/use_authtok/& remember=10/' -i /etc/pam.d/system-auth-permanent
      
Thursday, January 15, 2026 Monday, January 1, 1