cmd.exe (배치) 스크립트의 배열, 연결 목록 및 기타 데이터 구조
cmd.exe를 가지고 놀고 있었지만 그 도움으로 배열을 정의하는 방법에 대한 정보를 찾지 못했습니다.
간단한 변수를 정의하는 방법을 찾았습니다.
set a=10
echo %a%
하지만 배열, 연결 목록 등을 만들고 싶습니다.
그래서, cmd.exe에서 가능합니까 (내 말은 : cmd.exe에 배열 키워드가 있습니까?)
일부 알고리즘을 다음과 같이 실현하고 싶습니다.
- 버블 정렬
- 빠른 정렬
- 그놈 정렬
기타...
그래서 Cmd.exe에 참조 또는 인스턴스, 구조체 등이 있는지 알고 싶습니다.
그것의 도움이 가득하지 않은 원인 : /?
Cmd.exe는 Turing-Machine 정의에 의해 전체로 정의 될 수 있습니까? (튜링 완료)
확인. 오해되지 않도록 최대한 명확하게하겠습니다 ...
Windows 배치 파일에서 변수 이름 은 문자로 시작해야하며 유효한 문자를 포함 할 수 있습니다. 여기서 유효한 문자 는 문자와 숫자 외에 # $ '() * +,-.? @ [] _`{} ~입니다.
이것은 cmd.exe 관점에서 볼 때과 SET NORMAL_NAME=123
정확히 동일 SET A#$'()*+,-.?@[\]_{}~=123
하고 동일 함을 의미합니다 SET VECTOR[1]=123
. 세 가지 모두 정규 변수입니다. 이런 식 으로 배열 요소의 형태로 변수 이름을 작성 하는 것은 사용자 에게 달려 있습니다 .
set elem[1]=First element
set elem[2]=Second one
set elem[3]=The third one
이 방법을 echo %elem[2]%
보여줍니다 Second one
.
다른 변수 를 인덱스 로 사용하려면 퍼센트 기호로 묶인 변수를 값으로 대체하는 것이 왼쪽에서 오른쪽으로 구문 분석된다는 것을 알아야합니다 . 이것은 다음을 의미합니다.
set i=2
echo %elem[%i%]%
의 값을 보여 뜻하므로 원하는 결과를 제공하지 않는 elem[
한 다음 변수 i
의 값을 이어 ]
변수.
이 문제를 해결하려면 Delayed Expansion 을 사용해야합니다 . 즉 setlocal EnableDelayedExpansion
, 시작 부분에 명령을 삽입 하고 인덱스 변수를 백분율 기호로 묶고 배열 요소를 느낌표로 묶어야합니다.
setlocal EnableDelayedExpansion
set elem[1]=First element
set elem[2]=Second one
set elem[3]=The third one
set i=2
echo !elem[%i%]!
FOR 명령의 매개 변수를 색인으로 사용할 수도 있습니다 for /L %%i in (1,1,3) do echo !elem[%%i]!
.. ! index를 사용해야합니다! FOR 또는 IF : 내에서 인덱스가 변경 될 때 배열 요소에 값을 저장합니다 set elem[!index!]=New value
. FOR / IF 내에서 인덱스가 변경 될 때 요소의 값을 얻으려면 요소를 이중 퍼센트 기호로 묶고 명령 앞에 call
. 예를 들어 배열 요소의 범위를 왼쪽으로 네 자리 이동하려면 다음을 수행하십시오.
for /L %%i in (%start%,1,%end%) do (
set /A j=%%i + 4
call set elem[%%i]=%%elem[!j!]%%
)
이전 프로세스를 달성하는 또 다른 방법은 추가 FOR 명령을 사용하여 대체 가능한 동등한 매개 변수로 인덱스의 지연된 확장을 변경 한 다음 배열 요소에 대해 지연된 확장을 사용하는 것입니다. 이 방법은 이전 CALL보다 빠르게 실행됩니다.
for /L %%i in (%start%,1,%end%) do (
set /A j=%%i + 4
for %%j in (!j!) do set elem[%%i]=!elem[%%j]!
)
이렇게하면 배치 파일 이 배열을 관리하는 것처럼 작동 합니다. 여기서 중요한 점은 Batch가 배열을 관리하는지 여부를 논의하는 것이 아니라 다른 프로그래밍 언어와 동일한 방식으로 Batch 파일의 배열을 관리 할 수 있다는 사실입니다.
@echo off
setlocal EnableDelayedExpansion
rem Create vector with names of days
set i=0
for %%d in (Sunday Monday Tuesday Wednesday Thrusday Friday Saturday) do (
set /A i=i+1
set day[!i!]=%%d
)
rem Get current date and calculate DayOfWeek
for /F "tokens=1-3 delims=/" %%a in ("%date%") do (
set /A mm=10%%a %% 100, dd=10%%b %% 100, yy=%%c
)
if %mm% lss 3 set /A mm=mm+12, yy=yy-1
set /A a=yy/100, b=a/4, c=2-a+b, e=36525*(yy+4716)/100, f=306*(mm+1)/10, jdn=c+dd+e+f-1523, dow=jdn %% 7 + 1
echo Today is !day[%dow%]!, %date%
인덱스 값은 숫자로 제한되지 않지만 유효한 문자를 포함하는 모든 문자열 일 수 있습니다. 이 점을 통해 다른 프로그래밍 언어에서 연관 배열 이라고하는 것을 정의 할 수 있습니다 . 에서는 이 응답 연관 배열을 이용하여 문제를 해결하는 데 사용되는 방법의 상세한 설명이있다. 공백은 변수 이름에서 유효한 문자이므로 눈에 띄지 않을 수있는 변수 이름에 공백을 삽입하지 않도록주의해야합니다.
이 게시물 에서 배치 파일에서 배열 표기법을 사용해야하는 이유에 대해 설명했습니다 .
에서 이 게시물에 다음 텍스트 파일을 읽고 벡터의 라인의 인덱스를 저장하는 배치 파일이 수행하는 Buble 정렬 라인의 내용을 기반으로 한 벡터 요소의; 동등한 결과는 파일 내용에 대한 정렬입니다.
에서 이 게시물 파일에 저장 인덱스에 따라 배치의 기본 관계형 데이터베이스 응용 프로그램이 있습니다.
에서 이 게시물 조립 대형 데이터 구조가 TREE 명령의 형태로 하위 디렉토리 및 디스플레이 그것을에서 가져온 것을 배치에서 완벽한 여러 연결된 목록 응용 프로그램이 있습니다.
Windows 셸 스크립팅은 복잡한 데이터 구조는 말할 것도없고 배열과 함께 작동하도록 설계되지 않았습니다. 대부분의 경우 모든 것이 Windows 셸의 문자열이지만 루프를 사용하여 n
변수 VAR_1, VAR_2, VAR_3...
를 선언 하고 접두사를 필터링 VAR_
하거나 구분 된 문자열을 만든 다음 다음을 사용하여 배열을 "작업"하기 위해 수행 할 수있는 몇 가지 작업이 있습니다 . FOR
구분 된 문자열을 반복하는 구성.
마찬가지로, 동일한 기본 아이디어를 사용하여 like ITEM_NAME, ITEM_DATA
또는 w / e 와 같은 구조 체형 변수 집합을 만들 수 있습니다 . CMD에서 연관 배열을 시뮬레이션하는 방법에 대해 설명하는 이 링크 도 찾았습니다 .
그것에 관해서는 모두 끔찍하고 불편합니다. 명령 줄 셸은 무거운 프로그래밍을 위해 설계되지 않았습니다. @MatteoItalia에 동의합니다. 심각한 스크립팅이 필요한 경우 실제 스크립팅 언어를 사용하십시오.
얼마 전에 의사 배열을 사용하여 일괄 적으로 버블 정렬 구현을 만들었습니다. 목록 크기가 커짐에 따라 속도가 매우 느려지므로 (다른 배치 파일에서 사용하는 것을 인정할지라도) 왜 사용하는지 확실하지 않습니다. 나 자신에게 조금 도전하는 것이 더 많았다. 누군가 가 유용하다고 생각할 수 있습니다.
:: Bubblesort
:: Horribly inefficient for large lists
:: Dave Johnson implementation 05/04/2013
@echo off
setlocal enabledelayedexpansion
:: Number of entries to populate and sort
set maxvalue=50
:: Fill a list of vars with Random numbers and print them
for /l %%a in (1,1,%maxvalue%) do (
set /a tosort%%a=!random!
)
:: echo them
set tosort
:: Commence bubble sort
Echo Sorting...
set /a maxvalue-=1
set iterations=0
for /l %%a in (%maxvalue%,-1,1) do ( REM Decrease by 1 the number of checks each time as the top value will always float to the end
set hasswapped=0
for /l %%b in (1,1,%%a) do (
set /a next=%%b+1
set next=tosort!next!
set next=!next!
call :grabvalues tosort%%b !next!
rem echo comparing tosort%%b = !tosortvalue! and !next! = !nextvalue!
if !nextvalue! LSS !tosortvalue! (
rem set /a num_of_swaps+=1
rem echo Swapping !num_of_swaps!
set !next!=!tosortvalue!
set tosort%%b=!nextvalue!
set /a hasswapped+=1
)
)
set /a iterations+=1
if !hasswapped!==0 goto sorted
)
goto:eof
:grabvalues
set tosortvalue=!%1!
set nextvalue=!%2!
goto:eof
:sorted
::nice one our kid
set tosortvalue=
echo Iterations required: %iterations%
set tosort
endlocal
진지하게 말하면 배치에 배열이 있다는 것을 들어 본 적이 없습니다. 이상한 트릭으로 에뮬레이트 할 수 있지만 좋은 생각이라고는하지 않습니다.
참조 / 인스턴스 / 구조체는 실제 언어를위한 것입니다. cmd 스크립팅은 command.com이었던 매우 원시적 인 인터프리터를 통해 성장한 확장의 무리 일뿐입니다. 기본적인 스크립팅을 수행 할 수 있지만 여러 호출보다 더 복잡한 것은 다른 명령은 추하고 이해할 수 없게 될 운명입니다.
유일한 "고급"구조는 - 그것 - 모든 묘한이다 for
변수 대체의 이상한 "규칙"과 혼합 (, 루프, %var%
, %%var
, !var!
심지어 사소한 알고리즘에게의 컬렉션을 작성한다, 때문에 바보 파서의 다른 물건입니다) 이상한 해킹 (예 : quicksort 구현은 여기 참조 ).
제 팁은 건전한 방식으로 스크립팅을하고 싶다면 실제 스크립팅 언어를 사용하고 간단하고 빠른 해킹과 하위 호환성을 위해 배치를 남겨 두는 것입니다.
이 진술과 관련하여 :
간단한 변수를 정의하는 방법을 찾았습니다.
set a = 10 echo %a%
이것은 단순히 잘못되었습니다! 변수 a
는 비어 있고 (처음에는 비어 있다고 가정) echo %a%
반환 됩니다. ECHO is on.
호출 된 변수 a
SPACE는 실제로 값으로 설정됩니다 SPACE10
.
따라서 코드가 작동 SPACEs하려면 등호 기호 주변을 제거해야합니다 .
set a=10
echo %a%
모든 문자에 대해 안전하게 할당하려면 따옴표로 묶인 구문을 사용하십시오 ( 명령 확장이 활성화되어 있다고 가정하면 Windows 명령 프롬프트의 기본값입니다).
set "a=1&0"
echo(%a%
나머지 질문에 대해서는 Aacini 의 위대하고 포괄적 인 답변 을 읽는 것이 좋습니다 .
다음 프로그램은에서 벡터 (배열) 연산을 시뮬레이션 cmd
합니다. 여기에 제시된 서브 루틴은 초기에 프로그램 매개 변수를 배열에 저장하거나 " for
"루프 에서 파일 이름을 반복하여 배열에 저장하는 것과 같은 특수한 경우를 위해 설계되었습니다 . 이 경우 enabled delayed expansion
블록에서 " !
"문자 (매개 변수 값 또는 " for
"루프 변수 값에있는 경우)가 해석됩니다. 이것이 바로 이러한 경우에 disabled delayed expansion
블록 내에서 서브 루틴을 사용해야하는 이유입니다 .
@echo off
rem The subroutines presented bellow implement vectors (arrays) operations in CMD
rem Definition of a vector <v>:
rem v_0 - variable that stores the number of elements of the vector;
rem v_1..v_n, where n=v_0 - variables that store the values of the vector elements.
rem :::MAIN START:::
setlocal disabledelayedexpansion
rem Getting all the parameters passed to the program in the vector 'params':
rem Delayed expansion is left disabled in order not to interpret "!" in the program parameters' values (%1, %2, ... );
rem If a program parameter is not quoted, special characters in it (like "^", "&", "|") get interpreted at program launch.
:loop1
set "param=%~1"
if defined param (
call :VectorAddElementNext params param
shift
goto :loop1
)
rem Printing the vector 'params':
call :VectorPrint params
pause&echo.
rem After the vector variables are set, delayed expansion can be enabled and "!" are not interpreted in the vector variables's values:
echo Printing the elements of the vector 'params':
setlocal enabledelayedexpansion
if defined params_0 (
for /l %%i in (1,1,!params_0!) do (
echo params_%%i="!params_%%i!"
)
)
endlocal
pause&echo.
rem Setting the vector 'filenames' with the list of filenames in the current directory:
rem Delayed expansion is left disabled in order not to interpret "!" in the %%i variable's value;
for %%i in (*) do (
set "current_filename=%%~i"
call :VectorAddElementNext filenames current_filename
)
rem Printing the vector 'filenames':
call :VectorPrint filenames
pause&echo.
rem After the vector variables are set, delayed expansion can be enabled and "!" are not interpreted in the vector variables's values:
echo Printing the elements of the vector 'filenames':
setlocal enabledelayedexpansion
if defined filenames_0 (
for /l %%i in (1,1,!filenames_0!) do (
echo filenames_%%i="!filenames_%%i!"
)
)
endlocal
pause&echo.
endlocal
pause
rem :::MAIN END:::
goto :eof
:VectorAddElementNext
rem Vector Add Element Next
rem adds the string contained in variable %2 in the next element position (vector length + 1) in vector %1
(
setlocal enabledelayedexpansion
set "elem_value=!%2!"
set /a vector_length=%1_0
if not defined %1_0 set /a vector_length=0
set /a vector_length+=1
set elem_name=%1_!vector_length!
)
(
endlocal
set "%elem_name%=%elem_value%"
set %1_0=%vector_length%
goto :eof
)
:VectorAddElementDVNext
rem Vector Add Element Direct Value Next
rem adds the string %2 in the next element position (vector length + 1) in vector %1
(
setlocal enabledelayedexpansion
set "elem_value=%~2"
set /a vector_length=%1_0
if not defined %1_0 set /a vector_length=0
set /a vector_length+=1
set elem_name=%1_!vector_length!
)
(
endlocal
set "%elem_name%=%elem_value%"
set %1_0=%vector_length%
goto :eof
)
:VectorAddElement
rem Vector Add Element
rem adds the string contained in the variable %3 in the position contained in %2 (variable or direct value) in the vector %1
(
setlocal enabledelayedexpansion
set "elem_value=!%3!"
set /a elem_position=%2
set /a vector_length=%1_0
if not defined %1_0 set /a vector_length=0
if !elem_position! geq !vector_length! (
set /a vector_length=elem_position
)
set elem_name=%1_!elem_position!
)
(
endlocal
set "%elem_name%=%elem_value%"
if not "%elem_position%"=="0" set %1_0=%vector_length%
goto :eof
)
:VectorAddElementDV
rem Vector Add Element Direct Value
rem adds the string %3 in the position contained in %2 (variable or direct value) in the vector %1
(
setlocal enabledelayedexpansion
set "elem_value=%~3"
set /a elem_position=%2
set /a vector_length=%1_0
if not defined %1_0 set /a vector_length=0
if !elem_position! geq !vector_length! (
set /a vector_length=elem_position
)
set elem_name=%1_!elem_position!
)
(
endlocal
set "%elem_name%=%elem_value%"
if not "%elem_position%"=="0" set %1_0=%vector_length%
goto :eof
)
:VectorPrint
rem Vector Print
rem Prints all the elements names and values of the vector %1 on sepparate lines
(
setlocal enabledelayedexpansion
set /a vector_length=%1_0
if !vector_length! == 0 (
echo Vector "%1" is empty!
) else (
echo Vector "%1":
for /l %%i in (1,1,!vector_length!) do (
echo [%%i]: "!%1_%%i!"
)
)
)
(
endlocal
goto :eof
)
:VectorDestroy
rem Vector Destroy
rem Empties all the elements values of the vector %1
(
setlocal enabledelayedexpansion
set /a vector_length=%1_0
)
(
endlocal
if not %vector_length% == 0 (
for /l %%i in (1,1,%vector_length%) do (
set "%1_%%i="
)
set "%1_0="
)
goto :eof
)
또한 프로그램 매개 변수를 "배열"에 저장하거나 " for
"루프를 사용하여 디렉토리의 파일 이름을 통해 루프하고 !
제시된 서브 루틴을 사용 하지 않고 "배열"( 값에서 " "을 해석하지 않음)에 저장할 수 있습니다 . 위의 프로그램 :
@echo off
setlocal disabledelayedexpansion
rem Getting all the parameters passed to the program in the array 'params':
rem Delayed expansion is left disabled in order not to interpret "!" in the program parameters' values (%1, %2, ... );
rem If a program parameter is not quoted, special characters in it (like "^", "&", "|") get interpreted at program launch.
set /a count=1
:loop1
set "param=%~1"
if defined param (
set "params_%count%=%param%"
set /a count+=1
shift
goto :loop1
)
set /a params_0=count-1
echo.
rem After the array variables are set, delayed expansion can be enabled and "!" are not interpreted in the array variables's values:
rem Printing the array 'params':
echo Printing the elements of the array 'params':
setlocal enabledelayedexpansion
if defined params_0 (
for /l %%i in (1,1,!params_0!) do (
echo params_%%i="!params_%%i!"
)
)
endlocal
pause&echo.
rem Setting the array 'filenames' with the list of filenames in the current directory:
rem Delayed expansion is left disabled in order not to interpret "!" in the %%i variable's value;
set /a count=0
for %%i in (*) do (
set "current_filename=%%~i"
set /a count+=1
call set "filenames_%%count%%=%%current_filename%%"
)
set /a filenames_0=count
rem After the array variables are set, delayed expansion can be enabled and "!" are not interpreted in the array variables's values:
rem Printing the array 'filenames':
echo Printing the elements of the array 'filenames':
setlocal enabledelayedexpansion
if defined filenames_0 (
for /l %%i in (1,1,!filenames_0!) do (
echo filenames_%%i="!filenames_%%i!"
)
)
endlocal
endlocal
pause
goto :eof
TLDR :
나는 "For"루프와 "set"명령을 사용하여 변수의 구문 분석을 허용하는 아이디어에 부딪 혔고, 정렬 및 연결 목록 스타일의 의사 배열을 만들 수 있으며, 더 중요한 것은 구조와 유사한 의사 객체입니다.
일반적인 배치 의사 배열 및 구문 분석 방법 :
SET "_Arr.Names="Name 1" "Name 2" ... "Name N""
FOR %A IN (%_Arr.Names%) DO @( Echo.%~A )
REM Results:
REM Name 1
REM Name 2
REM ...
REM Name N
아래에서는 Dumb Pseudo Array와 수동으로 정렬 된 Pseudo Array를 만들고 DIR 명령의 출력을 포착하는 Ordered Pseudo Array를 만듭니다.
또한 Dumb Pseudo Arrays를 가져와 Ordered array로 변환합니다 (원래 Dumb Pseudo Array 변수는 나중에 제거).
그런 다음 더 많은 요소를 수동으로 포함하도록 정렬 된 모든 배열을 업데이트합니다.
마지막으로 7에서 9까지의 값에 대해 사전 정의 된 For L 루프를 수행하고 배열의 네 번째 예제 값을 인쇄하기 위해 Random 값을 생성하여 배열의 일부 값을 동적으로보고합니다.
노트 :
멤버를 추가하는 방법을 유지하는 변수를 만들어 멤버를 더 간단하게 추가합니다.
정렬 된 배열에서 Pseudo 객체로 작은 점프를하는 방법을 쉽게 볼 수 있도록이 점을 지적합니다.
@(
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO OFF
REM Manually Create a shortcut method to add more elements to a specific ordered array
SET "_Arr.Songs.Add=SET /A "_Arr.Songs.0+=1"&&CALL SET "_Arr.Songs.%%_Arr.Songs.0%%"
REM Define some 'dumb' Pseudo arrays
SET "_Arr.Names="Name 1" "Name 2" "Name 3" "Name 4" "Name 5" "Name 6" "Name 7" "Name 8""
SET "_Arr.States="AL" "AK" "AZ" "AR" "CA" "CO" "CT" "DE" "FL" "GA" "HI" "ID" "IL" "IN" "IA" "KS" "KY" "LA" "ME" "MD" "MA" "MI" "MN" "MS" "MO" "MT" "NE" "NV" "NH" "NJ" "NM" "NY" "NC" "ND" "OH" "OK" "OR" "PA" "RI" "SC" "SD" "TN" "TX" "UT" "VT" "VA" "WA" "WV" "WI" "WY""
)
REM Manually Create One Ordered Array
%_Arr.Songs.Add%=Hey Jude"
%_Arr.Songs.Add%=The Bartman"
%_Arr.Songs.Add%=Teenage Dirtbag"
%_Arr.Songs.Add%=Roundabout"
%_Arr.Songs.Add%=The Sound of Silence"
%_Arr.Songs.Add%=Jack and Diane"
%_Arr.Songs.Add%=One Angry Dwarf and 200 Solumn Faces"
REM Turn All Pre-Existing Normal Pseudo Arrays into Element Arrays
REM Since Ordered Arrays use Index 0, we can skip any manually created Ordered Arrays:
FOR /F "Tokens=2 Delims==." %%A IN ('SET _Arr. ^| FIND /V ".0=" ^| SORT') DO (
IF /I "%%~A" NEQ "!_TmpArrName!" (
SET "_TmpArrName=%%~A"
IF NOT DEFINED _Arr.!_TmpArrName!.Add (
REM Create a shortcut method to add more members to the array
SET "_Arr.!_TmpArrName!.Add=SET /A "_Arr.!_TmpArrName!.0+=1"&&CALL SET "_Arr.!_TmpArrName!.%%_Arr.!_TmpArrName!.0%%"
)
FOR %%a IN (!_Arr.%%~A!) DO (
CALL SET /A "_Arr.!_TmpArrName!.0+=1"
CALL SET "_Arr.!_TmpArrName!.%%_Arr.!_TmpArrName!.0%%=%%~a"
)
)
IF DEFINED _Arr.!_TmpArrName! (
REM Remove Unneeded Dumb Psuedo Array "_Arr.!_TmpArrName!"
SET "_Arr.!_TmpArrName!="
)
)
REM Create New Array of unknown Length from Command Output, and Store it as an Ordered Array
SET "_TmpArrName=WinDir"
FOR /F "Tokens=* Delims==." %%A IN ('Dir /B /A:D "C:\Windows"') DO (
IF NOT DEFINED _Arr.!_TmpArrName!.Add (
SET "_Arr.!_TmpArrName!.Add=SET /A "_Arr.!_TmpArrName!.0+=1"&&CALL SET "_Arr.!_TmpArrName!.%%_Arr.!_TmpArrName!.0%%"
)
CALL SET /A "_Arr.!_TmpArrName!.0+=1"
CALL SET "_Arr.!_TmpArrName!.%%_Arr.!_TmpArrName!.0%%=%%~A"
)
)
REM Manually Add additional Elements to the Ordered Arrays:
%_Arr.Names.Add%=Manual Name 1"
%_Arr.Names.Add%=Manual Name 2"
%_Arr.Names.Add%=Manual Name 3"
%_Arr.States.Add%=51st State"
%_Arr.States.Add%=52nd State"
%_Arr.States.Add%=53rd State"
%_Arr.Songs.Add%=Live and Let Die"
%_Arr.Songs.Add%=Baby Shark"
%_Arr.Songs.Add%=Safety Dance"
%_Arr.WinDir.Add%=Fake_Folder 1"
%_Arr.WinDir.Add%=Fake_Folder 2"
%_Arr.WinDir.Add%=Fake_Folder 3"
REM Test Output:
REM Use a For Loop to List Values 7 to 9 of each array and A Psuedo Rnadom 4th value
REM We are only interested in Ordered Arrays, so the .0 works nicely to locate those exclusively.
FOR /F "Tokens=2,4 Delims==." %%A IN ('SET _Arr. ^| FIND ".0=" ^| SORT') DO (
CALL :Get-Rnd %%~B
ECHO.
ECHO.%%~A 7 to 9, Plus !_Rnd#! - Psuedo Randomly Selected
FOR /L %%L IN (7,1,9) DO (
CALL Echo. * Element [%%L] of %%~A Pseudo Array = "%%_Arr.%%~A.%%L%%"
)
CALL Echo. * Random Element [!_Rnd#!] of %%~A Pseudo Array = "%%_Arr.%%~A.!_Rnd#!%%"
)
ENDLOCAL
GOTO :EOF
:Get-Rnd
SET /A "_RandMax=(32767 - ( ( ( 32767 %% %~1 ) + 1 ) %% %~1) )", "_Rnd#=!Random!"
IF /I !_Rnd#! GTR !_RandMax! ( GOTO :Get_Rnd# )
SET /A "_Rnd#%%=%~1"
GOTO :EOF
결과 예 :
Results:
Names 7 to 9, Plus 5 - Psuedo Randomly Selected
* Element [7] of Names Pseudo Array = "Name 7"
* Element [8] of Names Pseudo Array = "Name 8"
* Element [9] of Names Pseudo Array = "Manual Name 1"
* Random Element [5] of Names Pseudo Array = "Name 5"
Songs 7 to 9, Plus 5 - Psuedo Randomly Selected
* Element [7] of Songs Pseudo Array = "One Angry Dwarf and 200 Solumn Faces"
* Element [8] of Songs Pseudo Array = "Live and Let Die"
* Element [9] of Songs Pseudo Array = "Baby Shark"
* Random Element [5] of Songs Pseudo Array = "The Sound of Silence"
States 7 to 9, Plus 9 - Psuedo Randomly Selected
* Element [7] of States Pseudo Array = "CT"
* Element [8] of States Pseudo Array = "DE"
* Element [9] of States Pseudo Array = "FL"
* Random Element [9] of States Pseudo Array = "FL"
WinDir 7 to 9, Plus 26 - Psuedo Randomly Selected
* Element [7] of WinDir Pseudo Array = "assembly"
* Element [8] of WinDir Pseudo Array = "AUInstallAgent"
* Element [9] of WinDir Pseudo Array = "Boot"
* Random Element [26] of WinDir Pseudo Array = "Fonts"
처음에는 증분 카운터가있는 간단한 변수 줄인 Aacini와 유사한 작업을 수동으로 수행하거나 빠른 변수 목록에서 간단한 루프를 통해 할당합니다.
이것은 작은 2D 배열에 적합했습니다.
그러나 나는 특히 다중 값 콘텐츠가 필요할 때 긴 데이터 배열에 대해 고통을 느낍니다.
다차원 배열의 콘텐츠를 동적으로 일치시키고 채울 필요가있는시기에 대해서는 아무 말도하지 않습니다. 여기서 간단한 사용이 중단됩니다.
전반적으로 기능을 업데이트하거나 추가하는 데 필요한 여러 배열의 정보가 필요하게되었을 때 어려워 졌다는 것을 알게되었습니다.
이러한 배열은 본질적으로 변수로 내 보내야하는 하위 문자열의 목록이며 순서를 추가하거나 변경하면 코드를 변경하는 것을 의미합니다.
예를 들어 여러 FTP 서버에 로그인하고 특정 경로에서 X 일보다 오래된 파일을 삭제해야하는 시나리오를 생각해보십시오.
처음에는 다음과 같이 정의 할 하위 문자열의 간단한 배열을 만들 수 있습니다.
Site.##=[Array (String)] [Array (String)] @(
IP=[SubSting],
Username=[SubString],
Password[SubString])
또는이 예제 코드에 표시된대로.
(
SETOCAL
ECHO OFF
REM Manage Sites:
SET "Sites=13"
SET "MaxAge=28"
SET "Site.1="[IP]" "[User Name]" "[Password]" "[Path]""
SET "Site.2="[IP]" "[User Name]" "[Password]" "[Path]""
SET "Site.3="[IP]" "[User Name]" "[Password]" "[Path]""
REM ...
SET "Site.11="[IP]" "[User Name]" "[Password]" "[Path]""
SET "Site.12="[IP]" "[User Name]" "[Password]" "[Path]""
SET "Site.13="[IP]" "[User Name]" "[Password]" "[Path]""
)
FOR /L %%L IN (1,1,%Sites%) DO (
FOR /F "Tokens=*" %%A IN ('CALL ECHO %%Site.%%L%%') DO (
Echo. Pulled this example from a more complex example of my actual code, so the example variables may not need this loop, but it won't hurt to have if they don't need the extra expansion.
Call :Log
CALL :DeleteFTP %%~A
)
)
GOTO :EOF
:DeleteFTP
REM Simple ftp command for cygwin to delete the files found older than X days.
SET "FTPCMD="%~dp0lftp" %~1 -u %~2,%~3 -e "rm -rf %~4%MaxAge% "
FOR /F "Tokens=*" %%F IN ('"%FTPCMD% 2^>^&1"') DO @(
ECHO.%%~F
)
GOTO :EOF
자, 13 개의 사이트가 그렇게 나쁘지는 않습니다. 권리? 끝에 하나를 추가 한 다음 정보를 입력하고 완료 할 수 있습니다.
그런 다음보고를 위해 사이트 이름을 추가해야하므로 기능을 변경할 필요가 없도록 위치 5의 각 문자열에 다른 용어를 추가합니다.
::...
SET "Site.1="[IP]" "[User Name]" "[Password]" "[Path]" "[Site Name]""
::...
그런 다음 사이트 이름 (또는 IP)별로 순서를 유지해야한다는 것을 알게되었으므로 대부분의 사람들이 이름을 기억하기가 더 쉽고 다른 사람들이 볼 수 있도록해야 함) 순서를 변경하십시오. 모든 13 개 지점, 변수 확장 호출 및 함수.
::...
SET "Site.1="[Site Name]" "[IP]" "[User Name]" "[Password]" "[Path]""
::...
FOR /F "Tokens=*" %%A IN ('CALL ECHO %%Site.%%L%%')
::...
SET "FTPCMD="%~dp0lftp" %~2 -u %~3,%~4 -e "rm -rf %~5%MaxAge% "
::...
그런 다음 계속 악화됩니다.
- 동일한 사이트에서 다른 사용자를 사용하여 확인해야하는 디렉토리 수가 증가하기 시작합니다.
- 사이트마다 그리고 나중에 디렉토리마다 다른 보존 시간이 필요하다는 것을 알고 있습니다.
이 중 30, 40,50 개를 갖게되며 긴 문자열의 끝을보고 주변에 복사하는 등의 방법으로 어느 것이 있는지 기억하기 어렵습니다.
더 많은 경로 추가를 중지했지만 때로는 오래된 경로를 제거해야하거나 사라 졌을 때 문제가 발생합니다. 총 사이트 수를 업데이트하는 것을 잊은 경우 일부에서 스크립트 실행을 놓칠 수 있습니다.
디렉토리가 추가되거나 제거되면 각 사이트에서 디렉토리를 추가 / 제거해야하기 때문에 순서를 사용하기가 더 어렵고 ID가 쉽지 않기 때문에 사이트를 놓치기 쉽습니다.
정말 고통 스럽습니다. 동적 개체 집합이 필요한 경우에도 마찬가지입니다. 이것은 모두 수동입니다.
그래서 당신은 무엇을 할 수 있습니까? 음, 내가 한 일은 다음과 같습니다.
필자는 필요에 맞는 cmd 스크립트에서 일종의 가난한 사람의 구조 또는 객체 배열 (문자열)을 구현하는 데 의존했습니다.
IE 구조는 하위 속성 자체가있는 개체 일 수있는 여러 속성을 갖는 "사이트 개체"입니다. CMD는 실제로 객체 지향이 아니기 때문에 배열과 마찬가지로 약간의 문제입니다.
내가 시작한 예제가 내가 처음 시도한 것이기 때문에 다음과 같이 정의 할 중간 아말감 단계를 볼 수 있습니다.
eg: Site.[ID].[Object Property]=[Value, or array of values]
Site
.ID=[int]
.Name=[string]
.Path=[String]
.MaxAge=[Int]
.Details=[Array (String)] @(
IP=[SubSting],
Username=[SubString],
Password[SubString])
즉석에서 데이터 집합을 다시 정렬해야하는 문제를 해결하기 위해 내가 가지고 놀았 던 연결 목록 형식을 사용하는 것을 고려했지만 사이트 간의 순서를 유지하면서 각 사이트 그룹에 항목을 쉽게 추가하고 싶었 기 때문에 간단한 방법.
다음은이 단계의 또 다른 코드 예제입니다.
@(
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO OFF
SET "_SiteCount=0"
SET "_SiteID=0"
SET /A "_SiteID= !_SiteID! + 1"
SET "Site.!_SiteID!.MaxAge=Day5Ago"
SET "Site.!_SiteID!.Name=[SITE NAME HEADER FOR EMAIL]"
SET "Site.!_SiteID!.Detail="[IP]" "[UserName]" "[Password]" "[Path]""
REM ...
SET /A "_SiteID= !_SiteID! + 1"
SET "Site.!_SiteID!.MaxAge=Day15Ago"
SET "Site.!_SiteID!.Name=[SITE NAME HEADER FOR EMAIL]"
SET "Site.!_SiteID!.Detail="[IP]" "[UserName]" "[Password]" "[Path]""
)
CALL :Main
(
ENDLOCAL
Exit /b %eLvl%
)
:Main
REM In some forms of these the order isn't meaningful, but in others you need to follows the order and so we just count he number of site objects by counting one of their properties.
FOR /F %%A IN ('SET ^| FIND /I "Site." ^| FIND /I ".Name="') DO ( CALL SET /A "_SiteCount+=1" )
FOR /L %%L IN (1,1,34) DO (
CALL :PSGetDate_DaysAgo %%L
)
FOR /L %%L IN (1,1,%_SiteCount%) DO (
SET "Site.%%L.Create=NONE"
)
FOR /L %%L IN (1,1,%_SiteCount%) DO (
FOR /F "Tokens=*" %%A IN ('CALL ECHO ""%%Site.%%L.Name%%" %%Site.%%L.Detail%% "Site.%%L" "%%%%Site.%%L.MaxAge%%%%""') DO (
CALL ECHO CALL :DeleteFTP %%~A
CALL :DeleteFTP %%~A
)
)
CALL :SendMail "%EMLog%" "%_EMSubject%"
GOTO :EOF
:DeleteFTP
REM ECHO.IF "%~7" EQU "%skip%" (
IF "%~7" EQU "%skip%" (
GOTO :EOF
)
SET "FTPCMD="%~dp0lftp" %~2 -u %~3,%~4 -e "rm -rf %~5%~7 "
SET "FTPCMD=%FTPCMD%; bye""
FOR /F "Tokens=*" %%F IN ('"%FTPCMD% 2^>^&1"') DO @(
ECHO."%%F"
ECHO."%%~F"
REM CALL :Output "%Temp%\%~2_%~7.log" "%%F"
%OP% "%Temp%\%~2_%~7.log"
SET "FTPOut=%%~F"
)
GOTO :EOF
보시다시피 이러한 구조는 수동으로 적용하고 특정 순서대로 데이터를 표시해야하는 분기 계층 데이터 집합이있는 경우 매우 잘 작동합니다.
확실히 오늘은 구조의 기반을 스크립트의 이름으로 만듭니다. 이것이 더 유용하고 필요에 따라 정렬 된 배열을 사용할 수도 있고 사용하지 않을 수도 있기 때문입니다.
SET "_GUID=^%Time^%_^%Random:~-1^%^%Random:~-1^%^%Random:~-1^%^%Random:~-1^%^%Random:~-1^%^%Random:~-1^%^%Random:~-1^%^%Random:~-1^%^%Random:~-1^%"
eg: %~n0.[ObjectName].[Object Property].[Object Sub Property]=[Value, or array of values]
[Script Name]
.[Object Name](May Hold Count of Names)=[int]
.Name=[string]
.Paths(May Hold Count of IDs)=[INT]
.GUID=%_GUID%
.Path=String
.MaxAge=[Int]
.Details=[Array (String)] @(
IP=[SubSting],
Username=[SubString],
Password[SubString])
그러나 동적으로 생성 된 대규모 데이터 세트를 수집하고 미리 만들어진 범주로 그룹화 한 다음이를 혼합하여보고해야하는 경우는 어떻습니까?
여기에서도 이것들도 유용 할 수 있으며 필요에 따라 더 많은 속성을 추가하여 코드에서 즉석에서 빌드 할 수 있습니다.
FTP 삭제와 유사한 스크립트에서 여러 디렉토리의 크기를 확인해야합니다. 하나를 상당히 멍청하게 만들고 한 번만 확인하겠습니다.
@(
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO OFF
SET /A "_SiteID= !_SiteID! + 1"
SET "SiteName=SiteA"
SET "%~n0.!SiteName!=%%_SiteID%%
SET "%~n0.!SiteName!.SiteID=!_SiteID!
SET "%~n0.!SiteName!.Paths="PathA" "PathB" "PathC" "PathD" "PathE""
)
CALL :CheckFTP [FTP Login variables from source object including Site ID]
:CheckFTP
REM Not necessary to assign Variables, doing this for exposition only:
CALL SET "TempSiteName=%~6"
CALL SET "TempPaths=%%%~n0.%~1.Paths%%"
REM Clear the site Temp KB variables
FOR \F "Tokens=2* Delims== " %%H IN (%TempPaths% "Total" "Temp") DO (
CALL SET /A "%%%~n0.%~1.Paths.%%~H.KB=0"
)
FOR %%J IN (%TempPaths%) DO (
FOR /F "Tokens=1-2" %%F IN ('[FTP Command using source object options]') DO @(
CALL :SumSite "%~6" "%%~F" "%%~G"
FOR /F "Tokens=1,2,* delims=/" %%f IN ("%%~G") DO (
CALL :ConvertFolder "%~6" "%%~F" "%%~g" "%%~h" "%~6_%%~g_%%~h"
)
)
)
FOR /F "Tokens=3,4,7 Delims==_." %%g IN ('SET ^| FIND /I "%~6_" ^| FIND /I ".KB" ^| FIND /I /V "_."') DO (
CALL :WriteFolder "%%g/%%~h" "%TmpFile%" "%~6_%%~g_%%~h"
REM echo.CALL :WriteFolder "%%g/%%~h" "%TmpFile%" "%~6_%%~g_%%~h"
)
CALL :ConvertSite "%~1"
CALL :WriteTotalFolder "%~7" "%TmpFile%" "%~6"
CALL :SendMail "%TmpFile%" "Backup_%~1"
GOTO :EOF
:SumSite
CALL SET "TSumPaths=%%%~n0.%~1.Paths%% "Total""
FOR %%H IN (%TSumPaths%) DO (
CALL SET /A "%~n0.%~1.Paths.%%~H.KB=%%%~n0.%~1.Paths.%%~H.KB%%+%~2"
)
:SumSite
CALL SET "TSumPaths=%%%~n0.%~1.Paths%% "Total""
FOR %%H IN (%TSumPaths%) DO (
CALL SET /A "%~n0.%~1.Paths.%%~H.KB=%%%~n0.%~1.Paths.%%~H.KB%%+%~2"
)
GOTO :EOF
:ConvertFolder
REM Convert's Folder values to MB and GB
SET /A "%~1.Temp.KB=%~2"
CALL SET /A "%~1.Temp.MB=%%%~1.Temp.KB%%/1024"
CALL SET /A "%~1.Temp.GB=(%%%~1.Temp.KB%%/1024)/1024"
CALL SET /A "%~5.Temp.KB=%%%~5.Temp.KB%%+%~2"
CALL SET /A "%~5.Temp.MB=%%%~5.Temp.KB%%/1024"
CALL SET /A "%~5.Temp.GB=(%%%~5.Temp.KB%%/1024)/1024"
GOTO :EOF
:WriteFolder
CALL :PickGMKBytes "%~1" "%~2" "G" "M" "K" "%%%~3.Temp.GB%%" "%%%~3.Temp.MB%%" "%%%~3.Temp.KB%%"
GOTO :EOF
:PickGMKBytes
IF /I "%~6" NEQ "" (
IF /I "%~6"=="0" (
CALL :PickGMKBytes "%~1" "%~2" "%~4" "%~5" "%~6" "%~7" "%~8"
) ELSE (
CALL :Output "%~2" "%~6%~3 %~1"
)
) ELSE (
CALL :Output "%~2" "0B %~1"
)
GOTO :EOF
:ConvertSite
CALL SET "TempPaths=%%%~n0.%~1.Paths%%"
FOR %%V IN (%TempPaths% "Total") DO (
CALL SET /A "%~1.%%~V.MB=%%%~1.%%~V.KB%%/1024"
CALL SET /A "%~1.%%~V.GB=(%%%~1.%%~V.KB%%/1024)/1024"
)
GOTO :EOF
To be fair, this script example may not be very explicit in showing what is happening, and I had to make changes on the fly to fix a new object style, but essentially: It creates connection objects, and then dynamically extends them to include sub folders, and maintain running totals for each subfolder and site in KB, MB, and GB, and pics which of the values to report after summing up all of the directories for a given folder etc dynamically.
While i had to edit it a bit because this is also an earlier version of these as well, I thought it was one of the instances where it might best show the benefits. If I find a better example in one of my other scripts I might update it there as well.
@echo off
set array=
setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
set nl=^&echo(
set array=auto blue ^!nl!^
bycicle green ^!nl!^
buggy red
echo convert the String in indexed arrays
set /a index=0
for /F "tokens=1,2,3*" %%a in ( 'echo(!array!' ) do (
echo(vehicle[!index!]=%%a color[!index!]=%%b
set vehicle[!index!]=%%a
set color[!index!]=%%b
set /a index=!index!+1
)
echo use the arrays
echo(%vehicle[1]% %color[1]%
echo oder
set index=1
echo(!vehicle[%index%]! !color[%index%]!
'IT TIP' 카테고리의 다른 글
하위 폴더에서 루트 폴더의 이미지 선택 (0) | 2020.12.09 |
---|---|
교차하는 디스크 수를 계산하는 알고리즘 (0) | 2020.12.09 |
ArrayList에서 요소의 모든 발생 제거 (0) | 2020.12.09 |
리소스가 글꼴로 해석되지만 MIME 유형 application / x-font-woff로 전송 됨 (0) | 2020.12.09 |
Apache http 서버에 mod_proxy 설정 (0) | 2020.12.09 |