Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing Unicode parameters to Windows .bat file when rerunning it

My .bat file looks like this:

@echo off

CD /D "%~dp0"

if [%2]==[] (  
    set user=%USERNAME%
) else (
    set user=%2%
)

:getFile
if [%1]==[] (
    set /p file=Enter file name : 
) else (
    set file=%~f1
    echo File name: %~f1
)

:checkFile
for /f "useback tokens=*" %%a in ('%file%') do set file=%%~a

if not exist "%file%" (
    echo Error: Could not find file: %file%
    echo.
)

:: Check for admin permissions
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"

if '%errorlevel%' == '0' (
    goto gotAdmin
)

:: Rerun this batch with admin rights
echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
echo UAC.ShellExecute "cmd", "/c """"%~f0"" ""%file%"" ""%user%""""", "%CD%", "runas", 1 >> "%temp%\getadmin.vbs"
"%temp%\getadmin.vbs"
exit /B

:gotAdmin
if exist "%temp%\getadmin.vbs" ( del "%temp%\getadmin.vbs" )
pushd "%CD%"
CD /D "%~dp0"

echo.

:eof
pause
exit /B

I have these two test files:

  1. C:\Test\Folder\ファイル.txt
  2. C:\Test\フォルダ\File.txt

When I run the batch file above and drag 1 onto the cmd window I get:

enter image description here

, which is good.

When I do the same for 2, I get:

enter image description here

When I call UAC.ShellExecute, %file% isn't passed correctly.

How can I get around this problem?

like image 682
jpen Avatar asked Jun 26 '15 09:06

jpen


People also ask

What does %% bat mean?

%%a refers to the name of the variable your for loop will write to. Quoted from for /? : FOR %variable IN (set) DO command [command-parameters] %variable Specifies a single letter replaceable parameter. (set) Specifies a set of one or more files.

Can a batch file ask for input?

You are able to prompt a user for input using a Batch script function.

What encoding does batch file use?

Command line internal encoding (that changed with chcp) . bat Text Encoding.


3 Answers

My preferred way of starting a batch file with administrator permissions is to create a shortcut, and then mark that shortcut as requiring administrator permissions.

First right-click foo.bat, then create a shortcut. Open the properties for that shortcut, click the Advanced… button and enable Run as administrator.

This has a downside: you can't drag file names onto the resulting command prompt window. But you can drag a file onto the shortcut.

But what if I don't want or can't use a shortcut?

You can avoid the need to write arbitrary Unicode characters to the file by passing your file name as an argument to your script. Even if the VBS file is in ANSI encoding, the script host always uses Unicode internally.

So here is how you write the VBS file and run it:

:: Rerun this batch with admin rights
echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
echo UAC.ShellExecute "cmd", "/c """"%~f0"" """ + Wscript.Arguments.Item(0) + """ ""%user%""""", "%CD%", "runas", 1 >> "%temp%\getadmin.vbs"
"%temp%\getadmin.vbs" "%file%"
exit /B
like image 63
roeland Avatar answered Oct 27 '22 01:10

roeland


Try adding a CHCP (CHange Code Page) command to start of you batch file, using the UTF-8 code page 65001, e.g:

@echo off

chcp 65001
.
.
.

See here for a bit more info on code page identifiers: https://msdn.microsoft.com/en-us/library/windows/desktop/dd317756(v=vs.85).aspx

EDIT: You MUST also use a unicode capable font such as Lucida Console for your command window. Without this the command processor chokes on the unicode characters, and will either not find the files, or may display a "system cannot write to the specified device" error.

Click the window icon at the top-left of the command window, choose Defaults on the menu, then on the Fonts tab choose Lucida Console.

UPDATE - Test batch file and output below.

Here's the batch file I'm using to test this:

@echo off

chcp 65001

CD /D "%~dp0"


:getFile
if [%1]==[] (
    set /p file=Enter file name : 
) else (
    set file=%~f1
    echo File name: %~f1
)

:checkFile
for /f "useback tokens=*" %%a in ('%file%') do set file=%%~a

if not exist "%file%" (
    echo Error: Could not find file: %file%
    echo.
) else (
    echo Found file "%file%"
)

Here is the output from my test, when I drag firstly "C:\temp\test\ファイル.txt" into the window, then secondly "C:\temp\test\フォルダ\file2.txt".

My system is Win 7 Pro x64 SP1, with English UK settings.

Sample output

like image 32
beercohol Avatar answered Oct 26 '22 23:10

beercohol


Your problem is that the way you create your temporary VBS file means it is not a valid unicode file and so Windows doesn't know how to interpret the unicode name you have passed in.

Following beercohol's advice to use code page 65001, I still found that I could not access a file in a unicode directory. However, if I tried to create the file by hand with a unicode editor (e.g. using notepad and saving as a unicode encoding) and invoke that manual script instead of the autogenerated VBS file, it all just worked.

I've re-worked your script to use iconv to create a utf-16 file instead. Note that this script needs to be run with code page 65001 in order to work.

@echo off

CD /D "%~dp0"

if [%2]==[] (  
    set user=%USERNAME%
) else (
    set user=%2
)

:getFile
if [%1]==[] (
    set /p file=Enter file name : 
) else (
    set file=%~f1
    echo File name: %~f1
)

:checkFile
for /f "useback tokens=*" %%a in ('%file%') do set file=%%~a

if not exist "%file%" (
    echo Error: Could not find file: %file%
    echo.
)

:: Check for admin permissions
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"

if '%errorlevel%' == '0' (
    goto gotAdmin
)

:: Rerun this batch with admin rights
echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
echo UAC.ShellExecute "cmd", "/c """"%~f0"" ""%file%"" ""%user%""""", "%CD%", "runas", 1 >> "%temp%\getadmin.vbs"
iconv.exe -f utf-8 -t utf-16le "%temp%\getadmin.vbs" > "%temp%\getadmin2.vbs"
"%temp%\getadmin2.vbs"
exit /B

:gotAdmin
if exist "%temp%\getadmin.vbs" ( del "%temp%\getadmin.vbs" )
if exist "%temp%\getadmin2.vbs" ( del "%temp%\getadmin2.vbs" )
pushd "%CD%"
CD /D "%~dp0"

echo.

:eof
pause
exit /B
like image 42
Peter Brittain Avatar answered Oct 26 '22 23:10

Peter Brittain