Alexander Gromnitsky's Blog

I Asked ChatGPT How to Resolve a Symlink in PowerShell

Latest update:

The only job I have found for ChatGPT is to fix my grammar. For every other task, it only wastes my time.

Here's a very easy problem. Install vnu on Windows 10 so that:

  1. d:/opt/s/vnu/dist/ directory contains vnu.jar file.
  2. d:/opt/s/vnu/vnu.ps1 script runs the .jar.
  3. d:/opt/bin/vnu.ps1 is a symlink to the item #2.

d:/opt/bin/ is in PATH, hence when I run vnu.ps1 is should correctly invoke the .jar file. If you're a Windows user, you can probably write a corresponding powershell script with your eyes closed. But what if you're not a Windows user & are only familiar with bash? Suppose Google is down & all we have is ChatGPT?

Some preliminaries:

> scoop bucket add java
> scoop install oraclejdk

> mkdir -p d:/opt/s/vnu
> cd d:/opt/s/vnu
> wget.exe https://github.com/validator/validator/releases/download/20.6.30/vnu.jar_20.6.30.zip
> unzip *zip

OK, our 1st version is:

> pwd
Path
----
D:\opt\s\vnu

> cat vnu.ps1
$jar = "dist/vnu.jar"
java -jar $jar --asciiquotes --also-check-css $args
> ./vnu.ps1 --version
20.6.30

We, of course, are going to run the script in different directories, e.g.:

> cd ..
> .\vnu\vnu.ps1 --version
Error: Unable to access jarfile dist/vnu.jar

That was expected. What we are looking for here is to put $(dirname $(readlink -f $0)) into our script.

me: powershell equivalent of $(dirname $(readlink -f $0))

chatgpt: The PowerShell equivalent of the Bash command $(dirname $(readlink -f $0)), which returns the directory of the currently executing script, would be:

$scriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent

Hm, seems too easy:

> cat vnu/vnu.ps1
$dir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$jar = "$dir/dist/vnu.jar"
java -jar $jar --asciiquotes --also-check-css $args

But to test the new version, we also need to create a symlink to it. Um,

me: create a symlink in powershell

chatgpt: To create a symbolic link (symlink) in PowerShell, you can use the New-Item cmdlet with the -ItemType SymbolicLink parameter. The New-Item cmdlet creates a new file, directory, or other item, and the -ItemType parameter specifies the type of item to create. Here is an example:

New-Item -ItemType SymbolicLink -Path "C:\Users\Username\Desktop\Link" -Target "C:\Users\Username\Documents"

OK, seems fine.

> cd d:/opt/bin
> New-Item -ItemType SymbolicLink -target ..\s\vnu\vnu.ps1 -path vnu.ps1
New-Item : Administrator privilege required for this operation.

JFC, why, Microsoft, why? Anyway:

me: how do I run the command you provided as an administrator? is there a sudo equivalent?

chatgpt: To run a PowerShell command as an administrator, you can use the Start-Process cmdlet with the -Verb RunAs parameter. This parameter opens the process with administrative privileges, similar to the sudo command in Linux. Here's an example:

Start-Process powershell -Verb RunAs -ArgumentList "New-Item -ItemType SymbolicLink -Path 'C:\Users\Username\Desktop\Link' -Target 'C:\Users\Username\Documents'"

This looks rather idiotic, but when dutifully typed:

> Start-Process powershell -Verb RunAs -ArgumentList "New-Item -ItemType SymbolicLink -target ..\s\vnu\vnu.ps1 -path vnu.ps1"

it, of course, failed miserably. It indeed displayed a UAC prompt, but then a blue powershell terminal window blanked out for a fraction of a second. I presume it printed an error, but it was so quick to exit that I couldn't read the message. If you rerun the command as

> Start-Process powershell -Verb RunAs -ArgumentList "powershell"

it opens the blue powershell terminal with C:\Windows\system32 as the current working directory. Unhelpful if you're trying to create a relative symlink. Anyway I manually did 'cd' & rerun New-Item command (it worked). Then

> pwd
Path
----
D:\opt\bin

> .\vnu.ps1 -version
Error: Unable to access jarfile D:\opt\bin/dist/vnu.jar

Yep. So $MyInvocation.MyCommand.Definition != $(readlink -f $0). Somehow, I suspected that. Let's start from the beginning.

me: powershell equivalent of bash $0

chatgpt: The PowerShell equivalent of the Bash variable $0, which contains the name of the currently executing script, would be:

$MyInvocation.MyCommand.Name

OK, we already saw that split-path ~= basename, hence:

> cat ..\s\vnu\vnu.ps1
$dir = split-path $MyInvocation.MyCommand.Name
$jar = "$dir/dist/vnu.jar"
java -jar $jar --asciiquotes --also-check-css $args

Will it work if $MyInvocation.MyCommand is a symlink? No it won't:

> ./vnu.ps1
Error: Unable to access jarfile /dist/vnu.jar

WTF, $dir is empty!

me: briefly describe powershell's $MyInvocation.MyCommand

chatgpt: In PowerShell, the $MyInvocation automatic variable contains information about the current script or command invocation. One of the properties of $MyInvocation is MyCommand, which is an object that represents the current script or command.

The MyCommand object has several properties, including:

Name: The name of the script or command.

Definition: The full path of the script or command.

Fine:

> cat ..\s\vnu\vnu.ps1
$dir = split-path $MyInvocation.MyCommand.Definition
$jar = "$dir/dist/vnu.jar"
java -jar $jar --asciiquotes --also-check-css $args
> ./vnu.ps1
Error: Unable to access jarfile D:\opt\bin/dist/vnu.jar

Woo-hoo, we got "basename $0" working (again)! Amazing achievement.

me: powershell equivalent of linux readlink -f

chatgpt: The PowerShell equivalent of the Linux command readlink -f would be the following command:

(Get-Item -Path "path\to\file").FullName

Really? Well, what do I know, who am I to question our overlords:

> cat ..\s\vnu\vnu.ps1
$script = (Get-Item -Path $MyInvocation.MyCommand.Definition).FullName
$dir = split-path $script
$jar = "$dir/dist/vnu.jar"
java -jar $jar --asciiquotes --also-check-css $args
> ./vnu.ps1
Error: Unable to access jarfile D:\opt\bin/dist/vnu.jar

This is not even funny anymore, apparently Get-Item didn't resolve anything.

me: resolve a symlink in powershell

chatgpt: To resolve a symbolic link (symlink) and get the target path in PowerShell, you can use the Resolve-Path cmdlet with the -Path parameter. Here's an example:

$targetPath = Resolve-Path -Path "C:\path\to\symlink"

Nope:

> cat ..\s\vnu\vnu.ps1
$script = Resolve-Path -Path $MyInvocation.MyCommand.Definition
$dir = split-path $script
$jar = "$dir/dist/vnu.jar"
java -jar $jar --asciiquotes --also-check-css $args
> ./vnu.ps1
Error: Unable to access jarfile D:\opt\bin/dist/vnu.jar

Before I give up & google, let's try something different, ask the bot to rewrite a 100% working shell script:

me: rewrite a bash script in powershell:

#!/bin/sh
dir=$(dirname "$(readlink -f "$0")")
jar="$dir/dist/vnu.jar"
java -jar "$jar" --asciiquotes --also-check-css "$@"

chatgpt: Here's how you can rewrite the Bash script in PowerShell:

$dir = Split-Path -Path (Resolve-Path -Path $MyInvocation.MyCommand.Path) -Parent
$jar = "$dir\dist\vnu.jar"
java -jar $jar --asciiquotes --also-check-css $args

I think you can see the problem here. I couldn't take it any longer & googled 'resolve symlink in powershell'. Here's the final script that works:

> cat ..\s\vnu\vnu.ps1
$script = (get-item $MyInvocation.MyCommand.Definition).target
$dir = split-path $script
$jar = "$dir/dist/vnu.jar"
java -jar $jar --asciiquotes --also-check-css $args
> ./vnu.ps1 --version
20.6.30

Enjoy the future.


Tags: ойті
Authors: ag